libev_scheduler 0.1 → 0.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c76397791098c55780576c8b9140a89e63c58c0453b45529401fa3c6d755cd7f
4
- data.tar.gz: 57b0af6254897476b234fb5d3745d492e0d8b072b194782a22aee7f01c3cb233
3
+ metadata.gz: 921cd6937f62740e46edef5d1b3d135112276bab83149f896afb4ea6961a2f3f
4
+ data.tar.gz: afc0691185932a84c55ceac285eb24cb393f249e4c05176e744b4b3672a0e6c6
5
5
  SHA512:
6
- metadata.gz: f2ae40753c113be5e9e781711a543818c18e385df0ce7aae6bb0b8d8902aa8fdec3387462623a0e88870f9580184fa6f31ec1186d6e6cec399d7c9b45763ec72
7
- data.tar.gz: e9a391defe1e0487edf5bf215df668a4ce54fec3104b0f2ce192b7822e847c45a18232c26c4944bd4d8c13b65ecb5b8cfa3d90435c1fcbf44e1b5ac68eecd4e4
6
+ metadata.gz: cccc65981647ef19401aa9e168e7a9137a2cddbe93e15a5061329b1cdc17e63ba1bbb62758ee43ceb16635021119240d77d9cc127e78763928f7d3046b243232
7
+ data.tar.gz: a317d8f63c2fdb83eea494809980d8b44ab584e4d32fbfd76b57e5e02b954efd0014ff4232f327cc098cb7429cafac94a790116e9ca984b1600bac5180b2e2de
@@ -0,0 +1,25 @@
1
+ name: Tests
2
+
3
+ on: [push, pull_request]
4
+
5
+ jobs:
6
+ test:
7
+ name: >-
8
+ libev_scheduler ${{matrix.os}}, ${{matrix.ruby}}
9
+ runs-on: ${{matrix.os}}
10
+ strategy:
11
+ matrix:
12
+ os: [ ubuntu-latest ]
13
+ ruby: [ head, '3.0' ]
14
+ steps:
15
+ - uses: actions/checkout@v2
16
+ - name: Set up Ruby
17
+ uses: actions/setup-ruby@v1
18
+ with:
19
+ ruby-version: ${{matrix.ruby}}
20
+ - name: Install dependencies
21
+ run: bundle install
22
+ - name: Compile C-extension
23
+ run: bundle exec rake compile
24
+ - name: Run tests
25
+ run: bundle exec rake test
@@ -0,0 +1,3 @@
1
+ ## 0.2
2
+
3
+ - Update libev to version 4.33
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- libev_scheduler (0.1)
4
+ libev_scheduler (0.2)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
data/README.md CHANGED
@@ -1,5 +1,204 @@
1
1
  # libev_scheduler
2
2
 
3
- `libev_scheduler` is a libev-based fiber scheduler for Ruby 3.0 based on code from [Polyphony](https://github.com/digital-fabric/polyphony).
3
+ <p align="center">
4
+ <a href="http://rubygems.org/gems/libev_scheduler">
5
+ <img src="https://badge.fury.io/rb/libev_scheduler.svg" alt="Ruby gem">
6
+ </a>
7
+ <a href="https://github.com/digital-fabric/libev_scheduler/actions?query=workflow%3ATests">
8
+ <img src="https://github.com/digital-fabric/libev_scheduler/workflows/Tests/badge.svg" alt="Tests">
9
+ </a>
10
+ <a href="https://github.com/digital-fabric/libev_scheduler/blob/master/LICENSE">
11
+ <img src="https://img.shields.io/badge/license-MIT-blue.svg" alt="MIT License">
12
+ </a>
13
+ </p>
4
14
 
5
- More information coming soon...
15
+ `libev_scheduler` is a libev-based fiber scheduler for Ruby 3.0 based on code
16
+ extracted from [Polyphony](https://github.com/digital-fabric/libev_scheduler).
17
+
18
+ ## Installing
19
+
20
+ ```bash
21
+ $ gem install libev_scheduler
22
+ ```
23
+
24
+ ## Usage
25
+
26
+ ```ruby
27
+ Fiber.set_scheduler Libev::Scheduler.new
28
+
29
+ Fiber.schedule do
30
+ do_something_awesome
31
+ end
32
+ ```
33
+
34
+ Also have a look at the included tests and examples.
35
+
36
+ ## The scheduler implementation
37
+
38
+ The present gem uses
39
+ [libev](http://pod.tst.eu/http://cvs.schmorp.de/libev/ev.pod) to provide a
40
+ performant, cross-platform fiber scheduler implementation for Ruby 3.0. The
41
+ bundled libev is version 4.33, which includes an (experimental) io_uring
42
+ backend (more below about io_uring).
43
+
44
+ ## Some thoughts on the Ruby fiber scheduler interface
45
+
46
+ The fiber scheduler interface is a new feature in Ruby 3.0, aimed at
47
+ facilitating building fiber-based concurrent applications in Ruby. The current
48
+ [specification](https://docs.ruby-lang.org/en/master/Fiber/SchedulerInterface.html)
49
+ includes methods for:
50
+
51
+ - starting a non-blocking fiber
52
+ - waiting for an `IO` instance to become ready for reading or writing
53
+ - sleeping for a certain time duration
54
+ - waiting for a process to terminate
55
+ - otherwise pausing/resuming fibers (blocking/unblocking) for use with mutexes,
56
+ condition variables, queues etc.
57
+
58
+ However, the current design has some shortcomings that will need to be addressed
59
+ in order for this feature to become useful. Here are some of my thoughts on this
60
+ subject. Please do not take this as an attack on the wonderful work of the Ruby
61
+ core developers. Most probably I'm just some random guy being wrong on the
62
+ internet :-p.
63
+
64
+ ### Two kinds of fibers
65
+
66
+ One of the changes made as part of the work on the fiber scheduler interface in
67
+ Ruby 3.0 was to distinguish between two kinds of fibers: a normal, blocking
68
+ fiber; and a non-blocking fiber, which can be used in conjunction with the fiber
69
+ scheduler. While this was probably done for the sake of backward compatibility,
70
+ I believe this is an error. In introduces ambiguity where previously there was
71
+ none and makes the API more complex that it could have been.
72
+
73
+ It seems to me that a more logical solution to the problem of maintaining the
74
+ blocking behaviour by default, would be have been to set the non-blocking mode
75
+ at the level of the thread, instead of the fiber. That also would have allowed
76
+ using the main fiber (of a given thread) in a non-blocking manner (see below).
77
+
78
+ ### Performing blocking operations on the main fiber
79
+
80
+ While I didn't scratch the surface too much in terms of the limits of the fiber
81
+ scheduler interface, it looks pretty clear that the main fiber (in any thread)
82
+ cannot be used in a non-blocking manner. While fiber scheduler implementations
83
+ can in principle use `Fiber#transfer` to switch between fibers, which will allow
84
+ pausing and resuming the main fiber, it does not seem as if the current design
85
+ is really conductive to that.
86
+
87
+ ### I/O readiness
88
+
89
+ In and of itself, checking for I/O readiness is nice, but it does not allow us
90
+ to leverage the full power of io_uring on Linux or IOCP in Windows. In order to
91
+ leverage the advantages offered by io_uring, for instance, a fiber scheduler
92
+ should be able to do much more than just check for I/O readiness. It should be
93
+ able, rather, to *perform* I/O operations including read/write, send/recv,
94
+ connect and accept.
95
+
96
+ This is of course no small undertaking, but the current Ruby [native I/O
97
+ code](https://github.com/ruby/ruby/blob/master/io.c), currently at almost 14
98
+ KLOCS, is IMHO ripe for some overhauling, and maybe some separation of concerns.
99
+ It seems to me that the API layer for the `IO` class could be separated from the
100
+ code that does the actual reading/writing etc. This is indeed the approach I
101
+ took with [Polyphony](https://github.com/digital-fabric/polyphony/), which
102
+ provides the same `IO` API for developers, but performs the I/O ops using a
103
+ libev- or io_uring-based backend. This design can then reap all of the benefits
104
+ of using io_uring. Such an approach could also allow us to implement I/O using
105
+ IOCP on Windows (currently we can't because this requires files to be opened
106
+ with `WSA_FLAG_OVERLAPPED`).
107
+
108
+ This is also the reason I have decided not to release a native io_uring-backed
109
+ fiber scheduler implementation (with code extracted from Polyphony), since I
110
+ don't believe it can provide any real benefit in terms of performance. If I/O
111
+ readiness is all that the fiber scheduler can do, it's probably best to just use
112
+ a cross-platform implementation such as libev, which can then use io_uring
113
+ behind the scenes.
114
+
115
+ ### Waiting for processes
116
+
117
+ The problem with the current design is that the `#process_wait` method is
118
+ expected to return an instance of `Process::Status`. Unfortunately, this class
119
+ [cannot be
120
+ instantiated](https://github.com/ruby/ruby/blob/master/process.c#L8678), which
121
+ leads to a workaround using a separate thread.
122
+
123
+ Another difficulty associated with this is that for example on libev, a child
124
+ watcher can only be used on the default loop, which means only in the main
125
+ thread, as the child watcher implementation is based on receiving `SIGCHLD`.
126
+
127
+ An alternative solution would be to use `pidfd_open` and watch the returned fd
128
+ for readiness, but I don't know if this can be used on OSes other than linux.
129
+
130
+ While a cross-OS solution to the latter problem is potentially not too
131
+ difficult, the former problem is a real show-stopper. One solution might be to
132
+ change the API such that `#process_wait` returns an array containing the pid and
133
+ its status, for example. This can then be used to instantiate a
134
+ `Process::Status` object somewhere inside `Process.wait`.
135
+
136
+ ### On having multiple alternative fiber scheduler implementations
137
+
138
+ It is unclear to me that there is really a need for multiple fiber scheduler
139
+ implementations. It seems to me that an approach using multiple backends
140
+ selected according to the OS, is much more appropriate. It's not like there's
141
+ going to be a dozen different implementations of fiber schedulers. Actually,
142
+ libev fits really nicely here, since it already includes all those different
143
+ backends.
144
+
145
+
146
+ Besides, the term "fiber scheduler" is a bit of a misnomer, since it doesn't
147
+ really deal with *scheduling* fibers, but really with *performing blocking
148
+ operations in a fiber-aware manner*. The scheduling part is in many ways trivial
149
+ (i.e. the scheduler holds an array of fibers ready to run), but the performing
150
+ of blocking operations is [much more
151
+ involved](https://github.com/digital-fabric/polyphony/blob/master/ext/polyphony/backend_io_uring.c).
152
+
153
+ There is of course quite a bit of interaction between the scheduling part and
154
+ the blocking operations part, but then again to me a more sensible design would
155
+ have been to do everything related to scheduling inside of the Ruby core code,
156
+ and then offload everything else to a `BlockingOperationsBackend`
157
+ implementation. Here's what it might look like:
158
+
159
+ ```ruby
160
+ # example pseudo-code
161
+ class BlockingOperationsBackend
162
+ def poll(opts = {})
163
+ ev_run(@ev_loop)
164
+ end
165
+
166
+ def io_wait(io, opts)
167
+ fiber = Fiber.current
168
+ watcher = setup_watcher_for_io(io) do
169
+ Thread.current.schedule_fiber(fiber)
170
+ end
171
+ Fiber.yield
172
+ watcher.stop
173
+ end
174
+
175
+ ...
176
+ end
177
+ ```
178
+
179
+ The fiber scheduling part would provide a `Thread#schedule_fiber` method that
180
+ adds the given fiber to the thread's run queue, and the thread will know when to
181
+ call the backend's `#poll` method in order to poll for blocking operation
182
+ completions. For example:
183
+
184
+ ```ruby
185
+ # somewhere in Ruby's kischkas:
186
+ class Thread
187
+ def schedule_fiber(fiber)
188
+ @run_queue << fiber
189
+ end
190
+
191
+ def run_fiber_scheduler
192
+ @backend.poll
193
+ @run_queue.each { |f| f.resume }
194
+ end
195
+ end
196
+ ```
197
+
198
+ It seems to me this kind of design would be much easier to implement, and would
199
+ lead to a lot less code duplication. This design could also be extended later to
200
+ perform all kinds of blocking operations, such as reading/writing etc., as
201
+ discussed above.
202
+
203
+ Finally, such a design could also provide a C API for people writing extensions,
204
+ so they can rely on it whenever doing any blocking call.
@@ -1,8 +1,77 @@
1
1
  Revision history for libev, a high-performance and full-featured event loop.
2
2
 
3
+ TODO: for next ABI/API change, consider moving EV__IOFDSSET into io->fd instead and provide a getter.
4
+ TODO: document EV_TSTAMP_T
5
+
6
+ 4.33 Wed Mar 18 13:22:29 CET 2020
7
+ - no changes w.r.t. 4.32.
8
+
9
+ 4.32 (EV only)
10
+ - the 4.31 timerfd code wrongly changed the priority of the signal
11
+ fd watcher, which is usually harmless unless signal fds are
12
+ also used (found via cpan tester service).
13
+ - the documentation wrongly claimed that user may modify fd and events
14
+ members in io watchers when the watcher was stopped
15
+ (found by b_jonas).
16
+ - new ev_io_modify mutator which changes only the events member,
17
+ which can be faster. also added ev::io::set (int events) method
18
+ to ev++.h.
19
+ - officially allow a zero events mask for io watchers. this should
20
+ work with older libev versions as well but was not officially
21
+ allowed before.
22
+ - do not wake up every minute when timerfd is used to detect timejumps.
23
+ - do not wake up every minute when periodics are disabled and we have
24
+ a monotonic clock.
25
+ - support a lot more "uncommon" compile time configurations,
26
+ such as ev_embed enabled but ev_timer disabled.
27
+ - use a start/stop wrapper class to reduce code duplication in
28
+ ev++.h and make it needlessly more c++-y.
29
+ - the linux aio backend is no longer compiled in by default.
30
+ - update to libecb version 0x00010008.
31
+
32
+ 4.31 Fri Dec 20 21:58:29 CET 2019
33
+ - handle backends with minimum wait time a bit better by not
34
+ waiting in the presence of already-expired timers
35
+ (behaviour reported by Felipe Gasper).
36
+ - new feature: use timerfd to detect timejumps quickly,
37
+ can be disabled with the new EVFLAG_NOTIMERFD loop flag.
38
+ - document EV_USE_SIGNALFD feature macro.
39
+
40
+ 4.30 (EV only)
41
+ - change non-autoconf test for __kernel_rwf_t by testing
42
+ LINUX_VERSION_CODE, the most direct test I could find.
43
+ - fix a bug in the io_uring backend that polled the wrong
44
+ backend fd, causing it to not work in many cases.
45
+
46
+ 4.29 (EV only)
47
+ - add io uring autoconf and non-autoconf detection.
48
+ - disable io_uring when some header files are too old.
49
+
50
+ 4.28 (EV only)
51
+ - linuxaio backend resulted in random memory corruption
52
+ when loop is forked.
53
+ - linuxaio backend might have tried to cancel an iocb
54
+ multiple times (was unable to trigger this).
55
+ - linuxaio backend now employs a generation counter to
56
+ avoid handling spurious events from cancelled requests.
57
+ - io_cancel can return EINTR, deal with it. also, assume
58
+ io_submit also returns EINTR.
59
+ - fix some other minor bugs in linuxaio backend.
60
+ - ev_tstamp type can now be overriden by defining EV_TSTAMP_T.
61
+ - cleanup: replace expect_true/false and noinline by their
62
+ libecb counterparts.
63
+ - move syscall infrastructure from ev_linuxaio.c to ev.c.
64
+ - prepare io_uring integration.
65
+ - tweak ev_floor.
66
+ - epoll, poll, win32 Sleep and other places that use millisecond
67
+ reslution now all try to round up times.
68
+ - solaris port backend didn't compile.
69
+ - abstract time constants into their macros, for more flexibility.
70
+
3
71
  4.27 Thu Jun 27 22:43:44 CEST 2019
4
- - linux aio backend almost complete rewritten to work around its
72
+ - linux aio backend almost completely rewritten to work around its
5
73
  limitations.
74
+ - linux aio backend now requires linux 4.19+.
6
75
  - epoll backend now mandatory for linux aio backend.
7
76
  - fail assertions more aggressively on invalid fd's detected
8
77
  in the event loop, do not just silently fd_kill in case of
@@ -22,7 +91,7 @@ Revision history for libev, a high-performance and full-featured event loop.
22
91
  4.25 Fri Dec 21 07:49:20 CET 2018
23
92
  - INCOMPATIBLE CHANGE: EV_THROW was renamed to EV_NOEXCEPT
24
93
  (EV_THROW still provided) and now uses noexcept on C++11 or newer.
25
- - move the darwin select workaround highe rin ev.c, as newer versions of
94
+ - move the darwin select workaround higher in ev.c, as newer versions of
26
95
  darwin managed to break their broken select even more.
27
96
  - ANDROID => __ANDROID__ (reported by enh@google.com).
28
97
  - disable epoll_create1 on android because it has broken header files
@@ -116,7 +116,7 @@
116
116
  # undef EV_USE_POLL
117
117
  # define EV_USE_POLL 0
118
118
  # endif
119
-
119
+
120
120
  # if HAVE_EPOLL_CTL && HAVE_SYS_EPOLL_H
121
121
  # ifndef EV_USE_EPOLL
122
122
  # define EV_USE_EPOLL EV_FEATURE_BACKENDS
@@ -125,16 +125,25 @@
125
125
  # undef EV_USE_EPOLL
126
126
  # define EV_USE_EPOLL 0
127
127
  # endif
128
-
128
+
129
129
  # if HAVE_LINUX_AIO_ABI_H
130
130
  # ifndef EV_USE_LINUXAIO
131
- # define EV_USE_LINUXAIO EV_FEATURE_BACKENDS
131
+ # define EV_USE_LINUXAIO 0 /* was: EV_FEATURE_BACKENDS, always off by default */
132
132
  # endif
133
133
  # else
134
134
  # undef EV_USE_LINUXAIO
135
135
  # define EV_USE_LINUXAIO 0
136
136
  # endif
137
-
137
+
138
+ # if HAVE_LINUX_FS_H && HAVE_SYS_TIMERFD_H && HAVE_KERNEL_RWF_T
139
+ # ifndef EV_USE_IOURING
140
+ # define EV_USE_IOURING EV_FEATURE_BACKENDS
141
+ # endif
142
+ # else
143
+ # undef EV_USE_IOURING
144
+ # define EV_USE_IOURING 0
145
+ # endif
146
+
138
147
  # if HAVE_KQUEUE && HAVE_SYS_EVENT_H
139
148
  # ifndef EV_USE_KQUEUE
140
149
  # define EV_USE_KQUEUE EV_FEATURE_BACKENDS
@@ -143,7 +152,7 @@
143
152
  # undef EV_USE_KQUEUE
144
153
  # define EV_USE_KQUEUE 0
145
154
  # endif
146
-
155
+
147
156
  # if HAVE_PORT_H && HAVE_PORT_CREATE
148
157
  # ifndef EV_USE_PORT
149
158
  # define EV_USE_PORT EV_FEATURE_BACKENDS
@@ -179,7 +188,16 @@
179
188
  # undef EV_USE_EVENTFD
180
189
  # define EV_USE_EVENTFD 0
181
190
  # endif
182
-
191
+
192
+ # if HAVE_SYS_TIMERFD_H
193
+ # ifndef EV_USE_TIMERFD
194
+ # define EV_USE_TIMERFD EV_FEATURE_OS
195
+ # endif
196
+ # else
197
+ # undef EV_USE_TIMERFD
198
+ # define EV_USE_TIMERFD 0
199
+ # endif
200
+
183
201
  #endif
184
202
 
185
203
  /* OS X, in its infinite idiocy, actually HARDCODES
@@ -335,6 +353,22 @@
335
353
  # define EV_USE_PORT 0
336
354
  #endif
337
355
 
356
+ #ifndef EV_USE_LINUXAIO
357
+ # if __linux /* libev currently assumes linux/aio_abi.h is always available on linux */
358
+ # define EV_USE_LINUXAIO 0 /* was: 1, always off by default */
359
+ # else
360
+ # define EV_USE_LINUXAIO 0
361
+ # endif
362
+ #endif
363
+
364
+ #ifndef EV_USE_IOURING
365
+ # if __linux /* later checks might disable again */
366
+ # define EV_USE_IOURING 1
367
+ # else
368
+ # define EV_USE_IOURING 0
369
+ # endif
370
+ #endif
371
+
338
372
  #ifndef EV_USE_INOTIFY
339
373
  # if __linux && (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 4))
340
374
  # define EV_USE_INOTIFY EV_FEATURE_OS
@@ -367,6 +401,14 @@
367
401
  # endif
368
402
  #endif
369
403
 
404
+ #ifndef EV_USE_TIMERFD
405
+ # if __linux && (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 8))
406
+ # define EV_USE_TIMERFD EV_FEATURE_OS
407
+ # else
408
+ # define EV_USE_TIMERFD 0
409
+ # endif
410
+ #endif
411
+
370
412
  #if 0 /* debugging */
371
413
  # define EV_VERIFY 3
372
414
  # define EV_USE_4HEAP 1
@@ -409,6 +451,7 @@
409
451
  # define clock_gettime(id, ts) syscall (SYS_clock_gettime, (id), (ts))
410
452
  # undef EV_USE_MONOTONIC
411
453
  # define EV_USE_MONOTONIC 1
454
+ # define EV_NEED_SYSCALL 1
412
455
  # else
413
456
  # undef EV_USE_CLOCK_SYSCALL
414
457
  # define EV_USE_CLOCK_SYSCALL 0
@@ -432,6 +475,14 @@
432
475
  # define EV_USE_INOTIFY 0
433
476
  #endif
434
477
 
478
+ #if __linux && EV_USE_IOURING
479
+ # include <linux/version.h>
480
+ # if LINUX_VERSION_CODE < KERNEL_VERSION(4,14,0)
481
+ # undef EV_USE_IOURING
482
+ # define EV_USE_IOURING 0
483
+ # endif
484
+ #endif
485
+
435
486
  #if !EV_USE_NANOSLEEP
436
487
  /* hp-ux has it in sys/time.h, which we unconditionally include above */
437
488
  # if !defined _WIN32 && !defined __hpux
@@ -441,12 +492,29 @@
441
492
 
442
493
  #if EV_USE_LINUXAIO
443
494
  # include <sys/syscall.h>
444
- # if !SYS_io_getevents || !EV_USE_EPOLL /* ev_linxaio uses ev_poll.c:ev_epoll_create */
495
+ # if SYS_io_getevents && EV_USE_EPOLL /* linuxaio backend requires epoll backend */
496
+ # define EV_NEED_SYSCALL 1
497
+ # else
445
498
  # undef EV_USE_LINUXAIO
446
499
  # define EV_USE_LINUXAIO 0
447
500
  # endif
448
501
  #endif
449
502
 
503
+ #if EV_USE_IOURING
504
+ # include <sys/syscall.h>
505
+ # if !SYS_io_uring_setup && __linux && !__alpha
506
+ # define SYS_io_uring_setup 425
507
+ # define SYS_io_uring_enter 426
508
+ # define SYS_io_uring_wregister 427
509
+ # endif
510
+ # if SYS_io_uring_setup && EV_USE_EPOLL /* iouring backend requires epoll backend */
511
+ # define EV_NEED_SYSCALL 1
512
+ # else
513
+ # undef EV_USE_IOURING
514
+ # define EV_USE_IOURING 0
515
+ # endif
516
+ #endif
517
+
450
518
  #if EV_USE_INOTIFY
451
519
  # include <sys/statfs.h>
452
520
  # include <sys/inotify.h>
@@ -458,7 +526,7 @@
458
526
  #endif
459
527
 
460
528
  #if EV_USE_EVENTFD
461
- /* our minimum requirement is glibc 2.7 which has the stub, but not the header */
529
+ /* our minimum requirement is glibc 2.7 which has the stub, but not the full header */
462
530
  # include <stdint.h>
463
531
  # ifndef EFD_NONBLOCK
464
532
  # define EFD_NONBLOCK O_NONBLOCK
@@ -474,7 +542,7 @@ EV_CPP(extern "C") int (eventfd) (unsigned int initval, int flags);
474
542
  #endif
475
543
 
476
544
  #if EV_USE_SIGNALFD
477
- /* our minimum requirement is glibc 2.7 which has the stub, but not the header */
545
+ /* our minimum requirement is glibc 2.7 which has the stub, but not the full header */
478
546
  # include <stdint.h>
479
547
  # ifndef SFD_NONBLOCK
480
548
  # define SFD_NONBLOCK O_NONBLOCK
@@ -486,7 +554,7 @@ EV_CPP(extern "C") int (eventfd) (unsigned int initval, int flags);
486
554
  # define SFD_CLOEXEC 02000000
487
555
  # endif
488
556
  # endif
489
- EV_CPP (extern "C") int signalfd (int fd, const sigset_t *mask, int flags);
557
+ EV_CPP (extern "C") int (signalfd) (int fd, const sigset_t *mask, int flags);
490
558
 
491
559
  struct signalfd_siginfo
492
560
  {
@@ -495,7 +563,17 @@ struct signalfd_siginfo
495
563
  };
496
564
  #endif
497
565
 
498
- /**/
566
+ /* for timerfd, libev core requires TFD_TIMER_CANCEL_ON_SET &c */
567
+ #if EV_USE_TIMERFD
568
+ # include <sys/timerfd.h>
569
+ /* timerfd is only used for periodics */
570
+ # if !(defined (TFD_TIMER_CANCEL_ON_SET) && defined (TFD_CLOEXEC) && defined (TFD_NONBLOCK)) || !EV_PERIODIC_ENABLE
571
+ # undef EV_USE_TIMERFD
572
+ # define EV_USE_TIMERFD 0
573
+ # endif
574
+ #endif
575
+
576
+ /*****************************************************************************/
499
577
 
500
578
  #if EV_VERIFY >= 3
501
579
  # define EV_FREQUENT_CHECK ev_verify (EV_A)
@@ -510,18 +588,34 @@ struct signalfd_siginfo
510
588
  #define MIN_INTERVAL 0.0001220703125 /* 1/2**13, good till 4000 */
511
589
  /*#define MIN_INTERVAL 0.00000095367431640625 /* 1/2**20, good till 2200 */
512
590
 
513
- #define MIN_TIMEJUMP 1. /* minimum timejump that gets detected (if monotonic clock available) */
514
- #define MAX_BLOCKTIME 59.743 /* never wait longer than this time (to detect time jumps) */
591
+ #define MIN_TIMEJUMP 1. /* minimum timejump that gets detected (if monotonic clock available) */
592
+ #define MAX_BLOCKTIME 59.743 /* never wait longer than this time (to detect time jumps) */
593
+ #define MAX_BLOCKTIME2 1500001.07 /* same, but when timerfd is used to detect jumps, also safe delay to not overflow */
594
+
595
+ /* find a portable timestamp that is "always" in the future but fits into time_t.
596
+ * this is quite hard, and we are mostly guessing - we handle 32 bit signed/unsigned time_t,
597
+ * and sizes larger than 32 bit, and maybe the unlikely floating point time_t */
598
+ #define EV_TSTAMP_HUGE \
599
+ (sizeof (time_t) >= 8 ? 10000000000000. \
600
+ : 0 < (time_t)4294967295 ? 4294967295. \
601
+ : 2147483647.) \
515
602
 
516
- #define EV_TV_SET(tv,t) do { tv.tv_sec = (long)t; tv.tv_usec = (long)((t - tv.tv_sec) * 1e6); } while (0)
517
- #define EV_TS_SET(ts,t) do { ts.tv_sec = (long)t; ts.tv_nsec = (long)((t - ts.tv_sec) * 1e9); } while (0)
603
+ #ifndef EV_TS_CONST
604
+ # define EV_TS_CONST(nv) nv
605
+ # define EV_TS_TO_MSEC(a) a * 1e3 + 0.9999
606
+ # define EV_TS_FROM_USEC(us) us * 1e-6
607
+ # define EV_TV_SET(tv,t) do { tv.tv_sec = (long)t; tv.tv_usec = (long)((t - tv.tv_sec) * 1e6); } while (0)
608
+ # define EV_TS_SET(ts,t) do { ts.tv_sec = (long)t; ts.tv_nsec = (long)((t - ts.tv_sec) * 1e9); } while (0)
609
+ # define EV_TV_GET(tv) ((tv).tv_sec + (tv).tv_usec * 1e-6)
610
+ # define EV_TS_GET(ts) ((ts).tv_sec + (ts).tv_nsec * 1e-9)
611
+ #endif
518
612
 
519
613
  /* the following is ecb.h embedded into libev - use update_ev_c to update from an external copy */
520
614
  /* ECB.H BEGIN */
521
615
  /*
522
616
  * libecb - http://software.schmorp.de/pkg/libecb
523
617
  *
524
- * Copyright (©) 2009-2015 Marc Alexander Lehmann <libecb@schmorp.de>
618
+ * Copyright (©) 2009-2015,2018-2020 Marc Alexander Lehmann <libecb@schmorp.de>
525
619
  * Copyright (©) 2011 Emanuele Giaquinta
526
620
  * All rights reserved.
527
621
  *
@@ -562,15 +656,23 @@ struct signalfd_siginfo
562
656
  #define ECB_H
563
657
 
564
658
  /* 16 bits major, 16 bits minor */
565
- #define ECB_VERSION 0x00010006
659
+ #define ECB_VERSION 0x00010008
566
660
 
567
- #ifdef _WIN32
661
+ #include <string.h> /* for memcpy */
662
+
663
+ #if defined (_WIN32) && !defined (__MINGW32__)
568
664
  typedef signed char int8_t;
569
665
  typedef unsigned char uint8_t;
666
+ typedef signed char int_fast8_t;
667
+ typedef unsigned char uint_fast8_t;
570
668
  typedef signed short int16_t;
571
669
  typedef unsigned short uint16_t;
670
+ typedef signed int int_fast16_t;
671
+ typedef unsigned int uint_fast16_t;
572
672
  typedef signed int int32_t;
573
673
  typedef unsigned int uint32_t;
674
+ typedef signed int int_fast32_t;
675
+ typedef unsigned int uint_fast32_t;
574
676
  #if __GNUC__
575
677
  typedef signed long long int64_t;
576
678
  typedef unsigned long long uint64_t;
@@ -578,6 +680,8 @@ struct signalfd_siginfo
578
680
  typedef signed __int64 int64_t;
579
681
  typedef unsigned __int64 uint64_t;
580
682
  #endif
683
+ typedef int64_t int_fast64_t;
684
+ typedef uint64_t uint_fast64_t;
581
685
  #ifdef _WIN64
582
686
  #define ECB_PTRSIZE 8
583
687
  typedef uint64_t uintptr_t;
@@ -599,6 +703,14 @@ struct signalfd_siginfo
599
703
  #define ECB_GCC_AMD64 (__amd64 || __amd64__ || __x86_64 || __x86_64__)
600
704
  #define ECB_MSVC_AMD64 (_M_AMD64 || _M_X64)
601
705
 
706
+ #ifndef ECB_OPTIMIZE_SIZE
707
+ #if __OPTIMIZE_SIZE__
708
+ #define ECB_OPTIMIZE_SIZE 1
709
+ #else
710
+ #define ECB_OPTIMIZE_SIZE 0
711
+ #endif
712
+ #endif
713
+
602
714
  /* work around x32 idiocy by defining proper macros */
603
715
  #if ECB_GCC_AMD64 || ECB_MSVC_AMD64
604
716
  #if _ILP32
@@ -1114,6 +1226,44 @@ ecb_inline ecb_const uint32_t ecb_rotr32 (uint32_t x, unsigned int count) { retu
1114
1226
  ecb_inline ecb_const uint64_t ecb_rotl64 (uint64_t x, unsigned int count) { return (x >> (64 - count)) | (x << count); }
1115
1227
  ecb_inline ecb_const uint64_t ecb_rotr64 (uint64_t x, unsigned int count) { return (x << (64 - count)) | (x >> count); }
1116
1228
 
1229
+ #if ECB_CPP
1230
+
1231
+ inline uint8_t ecb_ctz (uint8_t v) { return ecb_ctz32 (v); }
1232
+ inline uint16_t ecb_ctz (uint16_t v) { return ecb_ctz32 (v); }
1233
+ inline uint32_t ecb_ctz (uint32_t v) { return ecb_ctz32 (v); }
1234
+ inline uint64_t ecb_ctz (uint64_t v) { return ecb_ctz64 (v); }
1235
+
1236
+ inline bool ecb_is_pot (uint8_t v) { return ecb_is_pot32 (v); }
1237
+ inline bool ecb_is_pot (uint16_t v) { return ecb_is_pot32 (v); }
1238
+ inline bool ecb_is_pot (uint32_t v) { return ecb_is_pot32 (v); }
1239
+ inline bool ecb_is_pot (uint64_t v) { return ecb_is_pot64 (v); }
1240
+
1241
+ inline int ecb_ld (uint8_t v) { return ecb_ld32 (v); }
1242
+ inline int ecb_ld (uint16_t v) { return ecb_ld32 (v); }
1243
+ inline int ecb_ld (uint32_t v) { return ecb_ld32 (v); }
1244
+ inline int ecb_ld (uint64_t v) { return ecb_ld64 (v); }
1245
+
1246
+ inline int ecb_popcount (uint8_t v) { return ecb_popcount32 (v); }
1247
+ inline int ecb_popcount (uint16_t v) { return ecb_popcount32 (v); }
1248
+ inline int ecb_popcount (uint32_t v) { return ecb_popcount32 (v); }
1249
+ inline int ecb_popcount (uint64_t v) { return ecb_popcount64 (v); }
1250
+
1251
+ inline uint8_t ecb_bitrev (uint8_t v) { return ecb_bitrev8 (v); }
1252
+ inline uint16_t ecb_bitrev (uint16_t v) { return ecb_bitrev16 (v); }
1253
+ inline uint32_t ecb_bitrev (uint32_t v) { return ecb_bitrev32 (v); }
1254
+
1255
+ inline uint8_t ecb_rotl (uint8_t v, unsigned int count) { return ecb_rotl8 (v, count); }
1256
+ inline uint16_t ecb_rotl (uint16_t v, unsigned int count) { return ecb_rotl16 (v, count); }
1257
+ inline uint32_t ecb_rotl (uint32_t v, unsigned int count) { return ecb_rotl32 (v, count); }
1258
+ inline uint64_t ecb_rotl (uint64_t v, unsigned int count) { return ecb_rotl64 (v, count); }
1259
+
1260
+ inline uint8_t ecb_rotr (uint8_t v, unsigned int count) { return ecb_rotr8 (v, count); }
1261
+ inline uint16_t ecb_rotr (uint16_t v, unsigned int count) { return ecb_rotr16 (v, count); }
1262
+ inline uint32_t ecb_rotr (uint32_t v, unsigned int count) { return ecb_rotr32 (v, count); }
1263
+ inline uint64_t ecb_rotr (uint64_t v, unsigned int count) { return ecb_rotr64 (v, count); }
1264
+
1265
+ #endif
1266
+
1117
1267
  #if ECB_GCC_VERSION(4,3) || (ECB_CLANG_BUILTIN(__builtin_bswap32) && ECB_CLANG_BUILTIN(__builtin_bswap64))
1118
1268
  #if ECB_GCC_VERSION(4,8) || ECB_CLANG_BUILTIN(__builtin_bswap16)
1119
1269
  #define ecb_bswap16(x) __builtin_bswap16 (x)
@@ -1194,6 +1344,78 @@ ecb_inline ecb_const ecb_bool ecb_big_endian (void) { return ecb_byteorder_he
1194
1344
  ecb_inline ecb_const ecb_bool ecb_little_endian (void);
1195
1345
  ecb_inline ecb_const ecb_bool ecb_little_endian (void) { return ecb_byteorder_helper () == 0x44332211; }
1196
1346
 
1347
+ /*****************************************************************************/
1348
+ /* unaligned load/store */
1349
+
1350
+ ecb_inline uint_fast16_t ecb_be_u16_to_host (uint_fast16_t v) { return ecb_little_endian () ? ecb_bswap16 (v) : v; }
1351
+ ecb_inline uint_fast32_t ecb_be_u32_to_host (uint_fast32_t v) { return ecb_little_endian () ? ecb_bswap32 (v) : v; }
1352
+ ecb_inline uint_fast64_t ecb_be_u64_to_host (uint_fast64_t v) { return ecb_little_endian () ? ecb_bswap64 (v) : v; }
1353
+
1354
+ ecb_inline uint_fast16_t ecb_le_u16_to_host (uint_fast16_t v) { return ecb_big_endian () ? ecb_bswap16 (v) : v; }
1355
+ ecb_inline uint_fast32_t ecb_le_u32_to_host (uint_fast32_t v) { return ecb_big_endian () ? ecb_bswap32 (v) : v; }
1356
+ ecb_inline uint_fast64_t ecb_le_u64_to_host (uint_fast64_t v) { return ecb_big_endian () ? ecb_bswap64 (v) : v; }
1357
+
1358
+ ecb_inline uint_fast16_t ecb_peek_u16_u (const void *ptr) { uint16_t v; memcpy (&v, ptr, sizeof (v)); return v; }
1359
+ ecb_inline uint_fast32_t ecb_peek_u32_u (const void *ptr) { uint32_t v; memcpy (&v, ptr, sizeof (v)); return v; }
1360
+ ecb_inline uint_fast64_t ecb_peek_u64_u (const void *ptr) { uint64_t v; memcpy (&v, ptr, sizeof (v)); return v; }
1361
+
1362
+ ecb_inline uint_fast16_t ecb_peek_be_u16_u (const void *ptr) { return ecb_be_u16_to_host (ecb_peek_u16_u (ptr)); }
1363
+ ecb_inline uint_fast32_t ecb_peek_be_u32_u (const void *ptr) { return ecb_be_u32_to_host (ecb_peek_u32_u (ptr)); }
1364
+ ecb_inline uint_fast64_t ecb_peek_be_u64_u (const void *ptr) { return ecb_be_u64_to_host (ecb_peek_u64_u (ptr)); }
1365
+
1366
+ ecb_inline uint_fast16_t ecb_peek_le_u16_u (const void *ptr) { return ecb_le_u16_to_host (ecb_peek_u16_u (ptr)); }
1367
+ ecb_inline uint_fast32_t ecb_peek_le_u32_u (const void *ptr) { return ecb_le_u32_to_host (ecb_peek_u32_u (ptr)); }
1368
+ ecb_inline uint_fast64_t ecb_peek_le_u64_u (const void *ptr) { return ecb_le_u64_to_host (ecb_peek_u64_u (ptr)); }
1369
+
1370
+ ecb_inline uint_fast16_t ecb_host_to_be_u16 (uint_fast16_t v) { return ecb_little_endian () ? ecb_bswap16 (v) : v; }
1371
+ ecb_inline uint_fast32_t ecb_host_to_be_u32 (uint_fast32_t v) { return ecb_little_endian () ? ecb_bswap32 (v) : v; }
1372
+ ecb_inline uint_fast64_t ecb_host_to_be_u64 (uint_fast64_t v) { return ecb_little_endian () ? ecb_bswap64 (v) : v; }
1373
+
1374
+ ecb_inline uint_fast16_t ecb_host_to_le_u16 (uint_fast16_t v) { return ecb_big_endian () ? ecb_bswap16 (v) : v; }
1375
+ ecb_inline uint_fast32_t ecb_host_to_le_u32 (uint_fast32_t v) { return ecb_big_endian () ? ecb_bswap32 (v) : v; }
1376
+ ecb_inline uint_fast64_t ecb_host_to_le_u64 (uint_fast64_t v) { return ecb_big_endian () ? ecb_bswap64 (v) : v; }
1377
+
1378
+ ecb_inline void ecb_poke_u16_u (void *ptr, uint16_t v) { memcpy (ptr, &v, sizeof (v)); }
1379
+ ecb_inline void ecb_poke_u32_u (void *ptr, uint32_t v) { memcpy (ptr, &v, sizeof (v)); }
1380
+ ecb_inline void ecb_poke_u64_u (void *ptr, uint64_t v) { memcpy (ptr, &v, sizeof (v)); }
1381
+
1382
+ ecb_inline void ecb_poke_be_u16_u (void *ptr, uint_fast16_t v) { ecb_poke_u16_u (ptr, ecb_host_to_be_u16 (v)); }
1383
+ ecb_inline void ecb_poke_be_u32_u (void *ptr, uint_fast32_t v) { ecb_poke_u32_u (ptr, ecb_host_to_be_u32 (v)); }
1384
+ ecb_inline void ecb_poke_be_u64_u (void *ptr, uint_fast64_t v) { ecb_poke_u64_u (ptr, ecb_host_to_be_u64 (v)); }
1385
+
1386
+ ecb_inline void ecb_poke_le_u16_u (void *ptr, uint_fast16_t v) { ecb_poke_u16_u (ptr, ecb_host_to_le_u16 (v)); }
1387
+ ecb_inline void ecb_poke_le_u32_u (void *ptr, uint_fast32_t v) { ecb_poke_u32_u (ptr, ecb_host_to_le_u32 (v)); }
1388
+ ecb_inline void ecb_poke_le_u64_u (void *ptr, uint_fast64_t v) { ecb_poke_u64_u (ptr, ecb_host_to_le_u64 (v)); }
1389
+
1390
+ #if ECB_CPP
1391
+
1392
+ inline uint8_t ecb_bswap (uint8_t v) { return v; }
1393
+ inline uint16_t ecb_bswap (uint16_t v) { return ecb_bswap16 (v); }
1394
+ inline uint32_t ecb_bswap (uint32_t v) { return ecb_bswap32 (v); }
1395
+ inline uint64_t ecb_bswap (uint64_t v) { return ecb_bswap64 (v); }
1396
+
1397
+ template<typename T> inline T ecb_be_to_host (T v) { return ecb_little_endian () ? ecb_bswap (v) : v; }
1398
+ template<typename T> inline T ecb_le_to_host (T v) { return ecb_big_endian () ? ecb_bswap (v) : v; }
1399
+ template<typename T> inline T ecb_peek (const void *ptr) { return *(const T *)ptr; }
1400
+ template<typename T> inline T ecb_peek_be (const void *ptr) { return ecb_be_to_host (ecb_peek <T> (ptr)); }
1401
+ template<typename T> inline T ecb_peek_le (const void *ptr) { return ecb_le_to_host (ecb_peek <T> (ptr)); }
1402
+ template<typename T> inline T ecb_peek_u (const void *ptr) { T v; memcpy (&v, ptr, sizeof (v)); return v; }
1403
+ template<typename T> inline T ecb_peek_be_u (const void *ptr) { return ecb_be_to_host (ecb_peek_u<T> (ptr)); }
1404
+ template<typename T> inline T ecb_peek_le_u (const void *ptr) { return ecb_le_to_host (ecb_peek_u<T> (ptr)); }
1405
+
1406
+ template<typename T> inline T ecb_host_to_be (T v) { return ecb_little_endian () ? ecb_bswap (v) : v; }
1407
+ template<typename T> inline T ecb_host_to_le (T v) { return ecb_big_endian () ? ecb_bswap (v) : v; }
1408
+ template<typename T> inline void ecb_poke (void *ptr, T v) { *(T *)ptr = v; }
1409
+ template<typename T> inline void ecb_poke_be (void *ptr, T v) { return ecb_poke <T> (ptr, ecb_host_to_be (v)); }
1410
+ template<typename T> inline void ecb_poke_le (void *ptr, T v) { return ecb_poke <T> (ptr, ecb_host_to_le (v)); }
1411
+ template<typename T> inline void ecb_poke_u (void *ptr, T v) { memcpy (ptr, &v, sizeof (v)); }
1412
+ template<typename T> inline void ecb_poke_be_u (void *ptr, T v) { return ecb_poke_u<T> (ptr, ecb_host_to_be (v)); }
1413
+ template<typename T> inline void ecb_poke_le_u (void *ptr, T v) { return ecb_poke_u<T> (ptr, ecb_host_to_le (v)); }
1414
+
1415
+ #endif
1416
+
1417
+ /*****************************************************************************/
1418
+
1197
1419
  #if ECB_GCC_VERSION(3,0) || ECB_C99
1198
1420
  #define ecb_mod(m,n) ((m) % (n) + ((m) % (n) < 0 ? (n) : 0))
1199
1421
  #else
@@ -1227,6 +1449,8 @@ ecb_inline ecb_const ecb_bool ecb_little_endian (void) { return ecb_byteorder_he
1227
1449
  #define ecb_array_length(name) (sizeof (name) / sizeof (name [0]))
1228
1450
  #endif
1229
1451
 
1452
+ /*****************************************************************************/
1453
+
1230
1454
  ecb_function_ ecb_const uint32_t ecb_binary16_to_binary32 (uint32_t x);
1231
1455
  ecb_function_ ecb_const uint32_t
1232
1456
  ecb_binary16_to_binary32 (uint32_t x)
@@ -1344,7 +1568,6 @@ ecb_binary32_to_binary16 (uint32_t x)
1344
1568
  || (defined __arm__ && (defined __ARM_EABI__ || defined __EABI__ || defined __VFP_FP__ || defined _WIN32_WCE || defined __ANDROID__)) \
1345
1569
  || defined __aarch64__
1346
1570
  #define ECB_STDFP 1
1347
- #include <string.h> /* for memcpy */
1348
1571
  #else
1349
1572
  #define ECB_STDFP 0
1350
1573
  #endif
@@ -1539,7 +1762,7 @@ ecb_binary32_to_binary16 (uint32_t x)
1539
1762
  #if ECB_MEMORY_FENCE_NEEDS_PTHREADS
1540
1763
  /* if your architecture doesn't need memory fences, e.g. because it is
1541
1764
  * single-cpu/core, or if you use libev in a project that doesn't use libev
1542
- * from multiple threads, then you can define ECB_AVOID_PTHREADS when compiling
1765
+ * from multiple threads, then you can define ECB_NO_THREADS when compiling
1543
1766
  * libev, in which cases the memory fences become nops.
1544
1767
  * alternatively, you can remove this #error and link against libpthread,
1545
1768
  * which will then provide the memory fences.
@@ -1553,18 +1776,80 @@ ecb_binary32_to_binary16 (uint32_t x)
1553
1776
  # define ECB_MEMORY_FENCE_RELEASE ECB_MEMORY_FENCE
1554
1777
  #endif
1555
1778
 
1556
- #define expect_false(cond) ecb_expect_false (cond)
1557
- #define expect_true(cond) ecb_expect_true (cond)
1558
- #define noinline ecb_noinline
1559
-
1560
1779
  #define inline_size ecb_inline
1561
1780
 
1562
1781
  #if EV_FEATURE_CODE
1563
1782
  # define inline_speed ecb_inline
1564
1783
  #else
1565
- # define inline_speed noinline static
1784
+ # define inline_speed ecb_noinline static
1566
1785
  #endif
1567
1786
 
1787
+ /*****************************************************************************/
1788
+ /* raw syscall wrappers */
1789
+
1790
+ #if EV_NEED_SYSCALL
1791
+
1792
+ #include <sys/syscall.h>
1793
+
1794
+ /*
1795
+ * define some syscall wrappers for common architectures
1796
+ * this is mostly for nice looks during debugging, not performance.
1797
+ * our syscalls return < 0, not == -1, on error. which is good
1798
+ * enough for linux aio.
1799
+ * TODO: arm is also common nowadays, maybe even mips and x86
1800
+ * TODO: after implementing this, it suddenly looks like overkill, but its hard to remove...
1801
+ */
1802
+ #if __GNUC__ && __linux && ECB_AMD64 && !EV_FEATURE_CODE
1803
+ /* the costly errno access probably kills this for size optimisation */
1804
+
1805
+ #define ev_syscall(nr,narg,arg1,arg2,arg3,arg4,arg5,arg6) \
1806
+ ({ \
1807
+ long res; \
1808
+ register unsigned long r6 __asm__ ("r9" ); \
1809
+ register unsigned long r5 __asm__ ("r8" ); \
1810
+ register unsigned long r4 __asm__ ("r10"); \
1811
+ register unsigned long r3 __asm__ ("rdx"); \
1812
+ register unsigned long r2 __asm__ ("rsi"); \
1813
+ register unsigned long r1 __asm__ ("rdi"); \
1814
+ if (narg >= 6) r6 = (unsigned long)(arg6); \
1815
+ if (narg >= 5) r5 = (unsigned long)(arg5); \
1816
+ if (narg >= 4) r4 = (unsigned long)(arg4); \
1817
+ if (narg >= 3) r3 = (unsigned long)(arg3); \
1818
+ if (narg >= 2) r2 = (unsigned long)(arg2); \
1819
+ if (narg >= 1) r1 = (unsigned long)(arg1); \
1820
+ __asm__ __volatile__ ( \
1821
+ "syscall\n\t" \
1822
+ : "=a" (res) \
1823
+ : "0" (nr), "r" (r1), "r" (r2), "r" (r3), "r" (r4), "r" (r5) \
1824
+ : "cc", "r11", "cx", "memory"); \
1825
+ errno = -res; \
1826
+ res; \
1827
+ })
1828
+
1829
+ #endif
1830
+
1831
+ #ifdef ev_syscall
1832
+ #define ev_syscall0(nr) ev_syscall (nr, 0, 0, 0, 0, 0, 0, 0)
1833
+ #define ev_syscall1(nr,arg1) ev_syscall (nr, 1, arg1, 0, 0, 0, 0, 0)
1834
+ #define ev_syscall2(nr,arg1,arg2) ev_syscall (nr, 2, arg1, arg2, 0, 0, 0, 0)
1835
+ #define ev_syscall3(nr,arg1,arg2,arg3) ev_syscall (nr, 3, arg1, arg2, arg3, 0, 0, 0)
1836
+ #define ev_syscall4(nr,arg1,arg2,arg3,arg4) ev_syscall (nr, 3, arg1, arg2, arg3, arg4, 0, 0)
1837
+ #define ev_syscall5(nr,arg1,arg2,arg3,arg4,arg5) ev_syscall (nr, 5, arg1, arg2, arg3, arg4, arg5, 0)
1838
+ #define ev_syscall6(nr,arg1,arg2,arg3,arg4,arg5,arg6) ev_syscall (nr, 6, arg1, arg2, arg3, arg4, arg5,arg6)
1839
+ #else
1840
+ #define ev_syscall0(nr) syscall (nr)
1841
+ #define ev_syscall1(nr,arg1) syscall (nr, arg1)
1842
+ #define ev_syscall2(nr,arg1,arg2) syscall (nr, arg1, arg2)
1843
+ #define ev_syscall3(nr,arg1,arg2,arg3) syscall (nr, arg1, arg2, arg3)
1844
+ #define ev_syscall4(nr,arg1,arg2,arg3,arg4) syscall (nr, arg1, arg2, arg3, arg4)
1845
+ #define ev_syscall5(nr,arg1,arg2,arg3,arg4,arg5) syscall (nr, arg1, arg2, arg3, arg4, arg5)
1846
+ #define ev_syscall6(nr,arg1,arg2,arg3,arg4,arg5,arg6) syscall (nr, arg1, arg2, arg3, arg4, arg5,arg6)
1847
+ #endif
1848
+
1849
+ #endif
1850
+
1851
+ /*****************************************************************************/
1852
+
1568
1853
  #define NUMPRI (EV_MAXPRI - EV_MINPRI + 1)
1569
1854
 
1570
1855
  #if EV_MINPRI == EV_MAXPRI
@@ -1622,7 +1907,7 @@ static EV_ATOMIC_T have_monotonic; /* did clock_gettime (CLOCK_MONOTONIC) work?
1622
1907
  #include <float.h>
1623
1908
 
1624
1909
  /* a floor() replacement function, should be independent of ev_tstamp type */
1625
- noinline
1910
+ ecb_noinline
1626
1911
  static ev_tstamp
1627
1912
  ev_floor (ev_tstamp v)
1628
1913
  {
@@ -1633,26 +1918,26 @@ ev_floor (ev_tstamp v)
1633
1918
  const ev_tstamp shift = sizeof (unsigned long) >= 8 ? 18446744073709551616. : 4294967296.;
1634
1919
  #endif
1635
1920
 
1636
- /* argument too large for an unsigned long? */
1637
- if (expect_false (v >= shift))
1921
+ /* special treatment for negative arguments */
1922
+ if (ecb_expect_false (v < 0.))
1923
+ {
1924
+ ev_tstamp f = -ev_floor (-v);
1925
+
1926
+ return f - (f == v ? 0 : 1);
1927
+ }
1928
+
1929
+ /* argument too large for an unsigned long? then reduce it */
1930
+ if (ecb_expect_false (v >= shift))
1638
1931
  {
1639
1932
  ev_tstamp f;
1640
1933
 
1641
1934
  if (v == v - 1.)
1642
- return v; /* very large number */
1935
+ return v; /* very large numbers are assumed to be integer */
1643
1936
 
1644
1937
  f = shift * ev_floor (v * (1. / shift));
1645
1938
  return f + ev_floor (v - f);
1646
1939
  }
1647
1940
 
1648
- /* special treatment for negative args? */
1649
- if (expect_false (v < 0.))
1650
- {
1651
- ev_tstamp f = -ev_floor (-v);
1652
-
1653
- return f - (f == v ? 0 : 1);
1654
- }
1655
-
1656
1941
  /* fits into an unsigned long */
1657
1942
  return (unsigned long)v;
1658
1943
  }
@@ -1665,7 +1950,7 @@ ev_floor (ev_tstamp v)
1665
1950
  # include <sys/utsname.h>
1666
1951
  #endif
1667
1952
 
1668
- noinline ecb_cold
1953
+ ecb_noinline ecb_cold
1669
1954
  static unsigned int
1670
1955
  ev_linux_version (void)
1671
1956
  {
@@ -1705,7 +1990,7 @@ ev_linux_version (void)
1705
1990
  /*****************************************************************************/
1706
1991
 
1707
1992
  #if EV_AVOID_STDIO
1708
- noinline ecb_cold
1993
+ ecb_noinline ecb_cold
1709
1994
  static void
1710
1995
  ev_printerr (const char *msg)
1711
1996
  {
@@ -1722,7 +2007,7 @@ ev_set_syserr_cb (void (*cb)(const char *msg) EV_NOEXCEPT) EV_NOEXCEPT
1722
2007
  syserr_cb = cb;
1723
2008
  }
1724
2009
 
1725
- noinline ecb_cold
2010
+ ecb_noinline ecb_cold
1726
2011
  static void
1727
2012
  ev_syserr (const char *msg)
1728
2013
  {
@@ -1804,7 +2089,7 @@ typedef struct
1804
2089
  unsigned char events; /* the events watched for */
1805
2090
  unsigned char reify; /* flag set when this ANFD needs reification (EV_ANFD_REIFY, EV__IOFDSET) */
1806
2091
  unsigned char emask; /* some backends store the actual kernel mask in here */
1807
- unsigned char unused;
2092
+ unsigned char eflags; /* flags field for use by backends */
1808
2093
  #if EV_USE_EPOLL
1809
2094
  unsigned int egen; /* generation counter to counter epoll bugs */
1810
2095
  #endif
@@ -1868,7 +2153,7 @@ typedef struct
1868
2153
 
1869
2154
  #else
1870
2155
 
1871
- EV_API_DECL ev_tstamp ev_rt_now = 0; /* needs to be initialised to make it a definition despite extern */
2156
+ EV_API_DECL ev_tstamp ev_rt_now = EV_TS_CONST (0.); /* needs to be initialised to make it a definition despite extern */
1872
2157
  #define VAR(name,decl) static decl;
1873
2158
  #include "ev_vars.h"
1874
2159
  #undef VAR
@@ -1878,8 +2163,8 @@ typedef struct
1878
2163
  #endif
1879
2164
 
1880
2165
  #if EV_FEATURE_API
1881
- # define EV_RELEASE_CB if (expect_false (release_cb)) release_cb (EV_A)
1882
- # define EV_ACQUIRE_CB if (expect_false (acquire_cb)) acquire_cb (EV_A)
2166
+ # define EV_RELEASE_CB if (ecb_expect_false (release_cb)) release_cb (EV_A)
2167
+ # define EV_ACQUIRE_CB if (ecb_expect_false (acquire_cb)) acquire_cb (EV_A)
1883
2168
  # define EV_INVOKE_PENDING invoke_cb (EV_A)
1884
2169
  #else
1885
2170
  # define EV_RELEASE_CB (void)0
@@ -1896,17 +2181,19 @@ ev_tstamp
1896
2181
  ev_time (void) EV_NOEXCEPT
1897
2182
  {
1898
2183
  #if EV_USE_REALTIME
1899
- if (expect_true (have_realtime))
2184
+ if (ecb_expect_true (have_realtime))
1900
2185
  {
1901
2186
  struct timespec ts;
1902
2187
  clock_gettime (CLOCK_REALTIME, &ts);
1903
- return ts.tv_sec + ts.tv_nsec * 1e-9;
2188
+ return EV_TS_GET (ts);
1904
2189
  }
1905
2190
  #endif
1906
2191
 
1907
- struct timeval tv;
1908
- gettimeofday (&tv, 0);
1909
- return tv.tv_sec + tv.tv_usec * 1e-6;
2192
+ {
2193
+ struct timeval tv;
2194
+ gettimeofday (&tv, 0);
2195
+ return EV_TV_GET (tv);
2196
+ }
1910
2197
  }
1911
2198
  #endif
1912
2199
 
@@ -1914,11 +2201,11 @@ inline_size ev_tstamp
1914
2201
  get_clock (void)
1915
2202
  {
1916
2203
  #if EV_USE_MONOTONIC
1917
- if (expect_true (have_monotonic))
2204
+ if (ecb_expect_true (have_monotonic))
1918
2205
  {
1919
2206
  struct timespec ts;
1920
2207
  clock_gettime (CLOCK_MONOTONIC, &ts);
1921
- return ts.tv_sec + ts.tv_nsec * 1e-9;
2208
+ return EV_TS_GET (ts);
1922
2209
  }
1923
2210
  #endif
1924
2211
 
@@ -1936,7 +2223,7 @@ ev_now (EV_P) EV_NOEXCEPT
1936
2223
  void
1937
2224
  ev_sleep (ev_tstamp delay) EV_NOEXCEPT
1938
2225
  {
1939
- if (delay > 0.)
2226
+ if (delay > EV_TS_CONST (0.))
1940
2227
  {
1941
2228
  #if EV_USE_NANOSLEEP
1942
2229
  struct timespec ts;
@@ -1946,7 +2233,7 @@ ev_sleep (ev_tstamp delay) EV_NOEXCEPT
1946
2233
  #elif defined _WIN32
1947
2234
  /* maybe this should round up, as ms is very low resolution */
1948
2235
  /* compared to select (µs) or nanosleep (ns) */
1949
- Sleep ((unsigned long)(delay * 1e3));
2236
+ Sleep ((unsigned long)(EV_TS_TO_MSEC (delay)));
1950
2237
  #else
1951
2238
  struct timeval tv;
1952
2239
 
@@ -1986,7 +2273,7 @@ array_nextsize (int elem, int cur, int cnt)
1986
2273
  return ncur;
1987
2274
  }
1988
2275
 
1989
- noinline ecb_cold
2276
+ ecb_noinline ecb_cold
1990
2277
  static void *
1991
2278
  array_realloc (int elem, void *base, int *cur, int cnt)
1992
2279
  {
@@ -2000,7 +2287,7 @@ array_realloc (int elem, void *base, int *cur, int cnt)
2000
2287
  memset ((void *)(base + offset), 0, sizeof (*(base)) * (count))
2001
2288
 
2002
2289
  #define array_needsize(type,base,cur,cnt,init) \
2003
- if (expect_false ((cnt) > (cur))) \
2290
+ if (ecb_expect_false ((cnt) > (cur))) \
2004
2291
  { \
2005
2292
  ecb_unused int ocur_ = (cur); \
2006
2293
  (base) = (type *)array_realloc \
@@ -2024,20 +2311,20 @@ array_realloc (int elem, void *base, int *cur, int cnt)
2024
2311
  /*****************************************************************************/
2025
2312
 
2026
2313
  /* dummy callback for pending events */
2027
- noinline
2314
+ ecb_noinline
2028
2315
  static void
2029
2316
  pendingcb (EV_P_ ev_prepare *w, int revents)
2030
2317
  {
2031
2318
  }
2032
2319
 
2033
- noinline
2320
+ ecb_noinline
2034
2321
  void
2035
2322
  ev_feed_event (EV_P_ void *w, int revents) EV_NOEXCEPT
2036
2323
  {
2037
2324
  W w_ = (W)w;
2038
2325
  int pri = ABSPRI (w_);
2039
2326
 
2040
- if (expect_false (w_->pending))
2327
+ if (ecb_expect_false (w_->pending))
2041
2328
  pendings [pri][w_->pending - 1].events |= revents;
2042
2329
  else
2043
2330
  {
@@ -2098,7 +2385,7 @@ fd_event (EV_P_ int fd, int revents)
2098
2385
  {
2099
2386
  ANFD *anfd = anfds + fd;
2100
2387
 
2101
- if (expect_true (!anfd->reify))
2388
+ if (ecb_expect_true (!anfd->reify))
2102
2389
  fd_event_nocheck (EV_A_ fd, revents);
2103
2390
  }
2104
2391
 
@@ -2116,8 +2403,20 @@ fd_reify (EV_P)
2116
2403
  {
2117
2404
  int i;
2118
2405
 
2406
+ /* most backends do not modify the fdchanges list in backend_modfiy.
2407
+ * except io_uring, which has fixed-size buffers which might force us
2408
+ * to handle events in backend_modify, causing fdchanges to be amended,
2409
+ * which could result in an endless loop.
2410
+ * to avoid this, we do not dynamically handle fds that were added
2411
+ * during fd_reify. that means that for those backends, fdchangecnt
2412
+ * might be non-zero during poll, which must cause them to not block.
2413
+ * to not put too much of a burden on other backends, this detail
2414
+ * needs to be handled in the backend.
2415
+ */
2416
+ int changecnt = fdchangecnt;
2417
+
2119
2418
  #if EV_SELECT_IS_WINSOCKET || EV_USE_IOCP
2120
- for (i = 0; i < fdchangecnt; ++i)
2419
+ for (i = 0; i < changecnt; ++i)
2121
2420
  {
2122
2421
  int fd = fdchanges [i];
2123
2422
  ANFD *anfd = anfds + fd;
@@ -2141,7 +2440,7 @@ fd_reify (EV_P)
2141
2440
  }
2142
2441
  #endif
2143
2442
 
2144
- for (i = 0; i < fdchangecnt; ++i)
2443
+ for (i = 0; i < changecnt; ++i)
2145
2444
  {
2146
2445
  int fd = fdchanges [i];
2147
2446
  ANFD *anfd = anfds + fd;
@@ -2152,7 +2451,7 @@ fd_reify (EV_P)
2152
2451
 
2153
2452
  anfd->reify = 0;
2154
2453
 
2155
- /*if (expect_true (o_reify & EV_ANFD_REIFY)) probably a deoptimisation */
2454
+ /*if (ecb_expect_true (o_reify & EV_ANFD_REIFY)) probably a deoptimisation */
2156
2455
  {
2157
2456
  anfd->events = 0;
2158
2457
 
@@ -2167,7 +2466,14 @@ fd_reify (EV_P)
2167
2466
  backend_modify (EV_A_ fd, o_events, anfd->events);
2168
2467
  }
2169
2468
 
2170
- fdchangecnt = 0;
2469
+ /* normally, fdchangecnt hasn't changed. if it has, then new fds have been added.
2470
+ * this is a rare case (see beginning comment in this function), so we copy them to the
2471
+ * front and hope the backend handles this case.
2472
+ */
2473
+ if (ecb_expect_false (fdchangecnt != changecnt))
2474
+ memmove (fdchanges, fdchanges + changecnt, (fdchangecnt - changecnt) * sizeof (*fdchanges));
2475
+
2476
+ fdchangecnt -= changecnt;
2171
2477
  }
2172
2478
 
2173
2479
  /* something about the given fd changed */
@@ -2176,9 +2482,9 @@ void
2176
2482
  fd_change (EV_P_ int fd, int flags)
2177
2483
  {
2178
2484
  unsigned char reify = anfds [fd].reify;
2179
- anfds [fd].reify |= flags;
2485
+ anfds [fd].reify = reify | flags;
2180
2486
 
2181
- if (expect_true (!reify))
2487
+ if (ecb_expect_true (!reify))
2182
2488
  {
2183
2489
  ++fdchangecnt;
2184
2490
  array_needsize (int, fdchanges, fdchangemax, fdchangecnt, array_needsize_noinit);
@@ -2211,7 +2517,7 @@ fd_valid (int fd)
2211
2517
  }
2212
2518
 
2213
2519
  /* called on EBADF to verify fds */
2214
- noinline ecb_cold
2520
+ ecb_noinline ecb_cold
2215
2521
  static void
2216
2522
  fd_ebadf (EV_P)
2217
2523
  {
@@ -2224,7 +2530,7 @@ fd_ebadf (EV_P)
2224
2530
  }
2225
2531
 
2226
2532
  /* called on ENOMEM in select/poll to kill some fds and retry */
2227
- noinline ecb_cold
2533
+ ecb_noinline ecb_cold
2228
2534
  static void
2229
2535
  fd_enomem (EV_P)
2230
2536
  {
@@ -2239,7 +2545,7 @@ fd_enomem (EV_P)
2239
2545
  }
2240
2546
 
2241
2547
  /* usually called after fork if backend needs to re-arm all fds from scratch */
2242
- noinline
2548
+ ecb_noinline
2243
2549
  static void
2244
2550
  fd_rearm_all (EV_P)
2245
2551
  {
@@ -2303,19 +2609,19 @@ downheap (ANHE *heap, int N, int k)
2303
2609
  ANHE *pos = heap + DHEAP * (k - HEAP0) + HEAP0 + 1;
2304
2610
 
2305
2611
  /* find minimum child */
2306
- if (expect_true (pos + DHEAP - 1 < E))
2612
+ if (ecb_expect_true (pos + DHEAP - 1 < E))
2307
2613
  {
2308
2614
  /* fast path */ (minpos = pos + 0), (minat = ANHE_at (*minpos));
2309
- if ( ANHE_at (pos [1]) < minat) (minpos = pos + 1), (minat = ANHE_at (*minpos));
2310
- if ( ANHE_at (pos [2]) < minat) (minpos = pos + 2), (minat = ANHE_at (*minpos));
2311
- if ( ANHE_at (pos [3]) < minat) (minpos = pos + 3), (minat = ANHE_at (*minpos));
2615
+ if ( minat > ANHE_at (pos [1])) (minpos = pos + 1), (minat = ANHE_at (*minpos));
2616
+ if ( minat > ANHE_at (pos [2])) (minpos = pos + 2), (minat = ANHE_at (*minpos));
2617
+ if ( minat > ANHE_at (pos [3])) (minpos = pos + 3), (minat = ANHE_at (*minpos));
2312
2618
  }
2313
2619
  else if (pos < E)
2314
2620
  {
2315
2621
  /* slow path */ (minpos = pos + 0), (minat = ANHE_at (*minpos));
2316
- if (pos + 1 < E && ANHE_at (pos [1]) < minat) (minpos = pos + 1), (minat = ANHE_at (*minpos));
2317
- if (pos + 2 < E && ANHE_at (pos [2]) < minat) (minpos = pos + 2), (minat = ANHE_at (*minpos));
2318
- if (pos + 3 < E && ANHE_at (pos [3]) < minat) (minpos = pos + 3), (minat = ANHE_at (*minpos));
2622
+ if (pos + 1 < E && minat > ANHE_at (pos [1])) (minpos = pos + 1), (minat = ANHE_at (*minpos));
2623
+ if (pos + 2 < E && minat > ANHE_at (pos [2])) (minpos = pos + 2), (minat = ANHE_at (*minpos));
2624
+ if (pos + 3 < E && minat > ANHE_at (pos [3])) (minpos = pos + 3), (minat = ANHE_at (*minpos));
2319
2625
  }
2320
2626
  else
2321
2627
  break;
@@ -2333,7 +2639,7 @@ downheap (ANHE *heap, int N, int k)
2333
2639
  ev_active (ANHE_w (he)) = k;
2334
2640
  }
2335
2641
 
2336
- #else /* 4HEAP */
2642
+ #else /* not 4HEAP */
2337
2643
 
2338
2644
  #define HEAP0 1
2339
2645
  #define HPARENT(k) ((k) >> 1)
@@ -2360,7 +2666,7 @@ downheap (ANHE *heap, int N, int k)
2360
2666
 
2361
2667
  heap [k] = heap [c];
2362
2668
  ev_active (ANHE_w (heap [k])) = k;
2363
-
2669
+
2364
2670
  k = c;
2365
2671
  }
2366
2672
 
@@ -2415,7 +2721,7 @@ reheap (ANHE *heap, int N)
2415
2721
 
2416
2722
  /*****************************************************************************/
2417
2723
 
2418
- /* associate signal watchers to a signal signal */
2724
+ /* associate signal watchers to a signal */
2419
2725
  typedef struct
2420
2726
  {
2421
2727
  EV_ATOMIC_T pending;
@@ -2431,7 +2737,7 @@ static ANSIG signals [EV_NSIG - 1];
2431
2737
 
2432
2738
  #if EV_SIGNAL_ENABLE || EV_ASYNC_ENABLE
2433
2739
 
2434
- noinline ecb_cold
2740
+ ecb_noinline ecb_cold
2435
2741
  static void
2436
2742
  evpipe_init (EV_P)
2437
2743
  {
@@ -2482,7 +2788,7 @@ evpipe_write (EV_P_ EV_ATOMIC_T *flag)
2482
2788
  {
2483
2789
  ECB_MEMORY_FENCE; /* push out the write before this function was called, acquire flag */
2484
2790
 
2485
- if (expect_true (*flag))
2791
+ if (ecb_expect_true (*flag))
2486
2792
  return;
2487
2793
 
2488
2794
  *flag = 1;
@@ -2569,7 +2875,7 @@ pipecb (EV_P_ ev_io *iow, int revents)
2569
2875
  ECB_MEMORY_FENCE;
2570
2876
 
2571
2877
  for (i = EV_NSIG - 1; i--; )
2572
- if (expect_false (signals [i].pending))
2878
+ if (ecb_expect_false (signals [i].pending))
2573
2879
  ev_feed_signal_event (EV_A_ i + 1);
2574
2880
  }
2575
2881
  #endif
@@ -2620,13 +2926,13 @@ ev_sighandler (int signum)
2620
2926
  ev_feed_signal (signum);
2621
2927
  }
2622
2928
 
2623
- noinline
2929
+ ecb_noinline
2624
2930
  void
2625
2931
  ev_feed_signal_event (EV_P_ int signum) EV_NOEXCEPT
2626
2932
  {
2627
2933
  WL w;
2628
2934
 
2629
- if (expect_false (signum <= 0 || signum >= EV_NSIG))
2935
+ if (ecb_expect_false (signum <= 0 || signum >= EV_NSIG))
2630
2936
  return;
2631
2937
 
2632
2938
  --signum;
@@ -2635,7 +2941,7 @@ ev_feed_signal_event (EV_P_ int signum) EV_NOEXCEPT
2635
2941
  /* it is permissible to try to feed a signal to the wrong loop */
2636
2942
  /* or, likely more useful, feeding a signal nobody is waiting for */
2637
2943
 
2638
- if (expect_false (signals [signum].loop != EV_A))
2944
+ if (ecb_expect_false (signals [signum].loop != EV_A))
2639
2945
  return;
2640
2946
  #endif
2641
2947
 
@@ -2729,6 +3035,57 @@ childcb (EV_P_ ev_signal *sw, int revents)
2729
3035
 
2730
3036
  /*****************************************************************************/
2731
3037
 
3038
+ #if EV_USE_TIMERFD
3039
+
3040
+ static void periodics_reschedule (EV_P);
3041
+
3042
+ static void
3043
+ timerfdcb (EV_P_ ev_io *iow, int revents)
3044
+ {
3045
+ struct itimerspec its = { 0 };
3046
+
3047
+ its.it_value.tv_sec = ev_rt_now + (int)MAX_BLOCKTIME2;
3048
+ timerfd_settime (timerfd, TFD_TIMER_ABSTIME | TFD_TIMER_CANCEL_ON_SET, &its, 0);
3049
+
3050
+ ev_rt_now = ev_time ();
3051
+ /* periodics_reschedule only needs ev_rt_now */
3052
+ /* but maybe in the future we want the full treatment. */
3053
+ /*
3054
+ now_floor = EV_TS_CONST (0.);
3055
+ time_update (EV_A_ EV_TSTAMP_HUGE);
3056
+ */
3057
+ #if EV_PERIODIC_ENABLE
3058
+ periodics_reschedule (EV_A);
3059
+ #endif
3060
+ }
3061
+
3062
+ ecb_noinline ecb_cold
3063
+ static void
3064
+ evtimerfd_init (EV_P)
3065
+ {
3066
+ if (!ev_is_active (&timerfd_w))
3067
+ {
3068
+ timerfd = timerfd_create (CLOCK_REALTIME, TFD_NONBLOCK | TFD_CLOEXEC);
3069
+
3070
+ if (timerfd >= 0)
3071
+ {
3072
+ fd_intern (timerfd); /* just to be sure */
3073
+
3074
+ ev_io_init (&timerfd_w, timerfdcb, timerfd, EV_READ);
3075
+ ev_set_priority (&timerfd_w, EV_MINPRI);
3076
+ ev_io_start (EV_A_ &timerfd_w);
3077
+ ev_unref (EV_A); /* watcher should not keep loop alive */
3078
+
3079
+ /* (re-) arm timer */
3080
+ timerfdcb (EV_A_ 0, 0);
3081
+ }
3082
+ }
3083
+ }
3084
+
3085
+ #endif
3086
+
3087
+ /*****************************************************************************/
3088
+
2732
3089
  #if EV_USE_IOCP
2733
3090
  # include "ev_iocp.c"
2734
3091
  #endif
@@ -2744,6 +3101,9 @@ childcb (EV_P_ ev_signal *sw, int revents)
2744
3101
  #if EV_USE_LINUXAIO
2745
3102
  # include "ev_linuxaio.c"
2746
3103
  #endif
3104
+ #if EV_USE_IOURING
3105
+ # include "ev_iouring.c"
3106
+ #endif
2747
3107
  #if EV_USE_POLL
2748
3108
  # include "ev_poll.c"
2749
3109
  #endif
@@ -2781,17 +3141,14 @@ ev_supported_backends (void) EV_NOEXCEPT
2781
3141
  {
2782
3142
  unsigned int flags = 0;
2783
3143
 
2784
- if (EV_USE_PORT ) flags |= EVBACKEND_PORT;
2785
- if (EV_USE_KQUEUE ) flags |= EVBACKEND_KQUEUE;
2786
- if (EV_USE_EPOLL ) flags |= EVBACKEND_EPOLL;
2787
-
2788
- #ifdef EV_USE_LINUXAIO
2789
- if (EV_USE_LINUXAIO) flags |= EVBACKEND_LINUXAIO;
2790
- #endif
3144
+ if (EV_USE_PORT ) flags |= EVBACKEND_PORT;
3145
+ if (EV_USE_KQUEUE ) flags |= EVBACKEND_KQUEUE;
3146
+ if (EV_USE_EPOLL ) flags |= EVBACKEND_EPOLL;
3147
+ if (EV_USE_LINUXAIO && ev_linux_version () >= 0x041300) flags |= EVBACKEND_LINUXAIO; /* 4.19+ */
3148
+ if (EV_USE_IOURING && ev_linux_version () >= 0x050601 ) flags |= EVBACKEND_IOURING; /* 5.6.1+ */
3149
+ if (EV_USE_POLL ) flags |= EVBACKEND_POLL;
3150
+ if (EV_USE_SELECT ) flags |= EVBACKEND_SELECT;
2791
3151
 
2792
- if (EV_USE_POLL ) flags |= EVBACKEND_POLL;
2793
- if (EV_USE_SELECT ) flags |= EVBACKEND_SELECT;
2794
-
2795
3152
  return flags;
2796
3153
  }
2797
3154
 
@@ -2801,24 +3158,27 @@ ev_recommended_backends (void) EV_NOEXCEPT
2801
3158
  {
2802
3159
  unsigned int flags = ev_supported_backends ();
2803
3160
 
2804
- #if defined(__APPLE__) && (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_14)
2805
- /* apple has a poor track record but post 10.12.2 it seems to work sufficiently well */
2806
- #elif defined(__NetBSD__)
2807
- /* kqueue is borked on everything but netbsd apparently */
2808
- /* it usually doesn't work correctly on anything but sockets and pipes */
2809
- #else
3161
+ /* apple has a poor track record but post 10.12.2 it seems to work sufficiently well */
3162
+ #if defined(__APPLE__) && (MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_14)
2810
3163
  /* only select works correctly on that "unix-certified" platform */
2811
3164
  flags &= ~EVBACKEND_KQUEUE; /* horribly broken, even for sockets */
2812
3165
  flags &= ~EVBACKEND_POLL; /* poll is based on kqueue from 10.5 onwards */
3166
+ #elif !defined(__NetBSD__)
3167
+ /* kqueue is borked on everything but netbsd apparently */
3168
+ /* it usually doesn't work correctly on anything but sockets and pipes */
3169
+ flags &= ~EVBACKEND_KQUEUE;
2813
3170
  #endif
2814
3171
 
2815
3172
  #ifdef __FreeBSD__
2816
3173
  flags &= ~EVBACKEND_POLL; /* poll return value is unusable (http://forums.freebsd.org/archive/index.php/t-10270.html) */
2817
3174
  #endif
2818
3175
 
2819
- /* TODO: linuxaio is very experimental */
2820
- #if !EV_RECOMMEND_LINUXAIO
3176
+ #ifdef __linux__
3177
+ /* NOTE: linuxaio is very experimental, never recommend */
2821
3178
  flags &= ~EVBACKEND_LINUXAIO;
3179
+
3180
+ /* NOTE: io_uring is super experimental, never recommend */
3181
+ flags &= ~EVBACKEND_IOURING;
2822
3182
  #endif
2823
3183
 
2824
3184
  return flags;
@@ -2828,12 +3188,14 @@ ecb_cold
2828
3188
  unsigned int
2829
3189
  ev_embeddable_backends (void) EV_NOEXCEPT
2830
3190
  {
2831
- int flags = EVBACKEND_EPOLL | EVBACKEND_KQUEUE | EVBACKEND_PORT;
3191
+ int flags = EVBACKEND_EPOLL | EVBACKEND_KQUEUE | EVBACKEND_PORT | EVBACKEND_IOURING;
2832
3192
 
2833
3193
  /* epoll embeddability broken on all linux versions up to at least 2.6.23 */
2834
3194
  if (ev_linux_version () < 0x020620) /* disable it on linux < 2.6.32 */
2835
3195
  flags &= ~EVBACKEND_EPOLL;
2836
3196
 
3197
+ /* EVBACKEND_LINUXAIO is theoretically embeddable, but suffers from a performance overhead */
3198
+
2837
3199
  return flags;
2838
3200
  }
2839
3201
 
@@ -2895,7 +3257,7 @@ ev_set_loop_release_cb (EV_P_ void (*release)(EV_P) EV_NOEXCEPT, void (*acquire)
2895
3257
  #endif
2896
3258
 
2897
3259
  /* initialise a loop structure, must be zero-initialised */
2898
- noinline ecb_cold
3260
+ ecb_noinline ecb_cold
2899
3261
  static void
2900
3262
  loop_init (EV_P_ unsigned int flags) EV_NOEXCEPT
2901
3263
  {
@@ -2960,6 +3322,9 @@ loop_init (EV_P_ unsigned int flags) EV_NOEXCEPT
2960
3322
  #if EV_USE_SIGNALFD
2961
3323
  sigfd = flags & EVFLAG_SIGNALFD ? -2 : -1;
2962
3324
  #endif
3325
+ #if EV_USE_TIMERFD
3326
+ timerfd = flags & EVFLAG_NOTIMERFD ? -1 : -2;
3327
+ #endif
2963
3328
 
2964
3329
  if (!(flags & EVBACKEND_MASK))
2965
3330
  flags |= ev_recommended_backends ();
@@ -2973,6 +3338,9 @@ loop_init (EV_P_ unsigned int flags) EV_NOEXCEPT
2973
3338
  #if EV_USE_KQUEUE
2974
3339
  if (!backend && (flags & EVBACKEND_KQUEUE )) backend = kqueue_init (EV_A_ flags);
2975
3340
  #endif
3341
+ #if EV_USE_IOURING
3342
+ if (!backend && (flags & EVBACKEND_IOURING )) backend = iouring_init (EV_A_ flags);
3343
+ #endif
2976
3344
  #if EV_USE_LINUXAIO
2977
3345
  if (!backend && (flags & EVBACKEND_LINUXAIO)) backend = linuxaio_init (EV_A_ flags);
2978
3346
  #endif
@@ -3010,7 +3378,7 @@ ev_loop_destroy (EV_P)
3010
3378
 
3011
3379
  #if EV_CLEANUP_ENABLE
3012
3380
  /* queue cleanup watchers (and execute them) */
3013
- if (expect_false (cleanupcnt))
3381
+ if (ecb_expect_false (cleanupcnt))
3014
3382
  {
3015
3383
  queue_events (EV_A_ (W *)cleanups, cleanupcnt, EV_CLEANUP);
3016
3384
  EV_INVOKE_PENDING;
@@ -3039,6 +3407,11 @@ ev_loop_destroy (EV_P)
3039
3407
  close (sigfd);
3040
3408
  #endif
3041
3409
 
3410
+ #if EV_USE_TIMERFD
3411
+ if (ev_is_active (&timerfd_w))
3412
+ close (timerfd);
3413
+ #endif
3414
+
3042
3415
  #if EV_USE_INOTIFY
3043
3416
  if (fs_fd >= 0)
3044
3417
  close (fs_fd);
@@ -3056,6 +3429,9 @@ ev_loop_destroy (EV_P)
3056
3429
  #if EV_USE_KQUEUE
3057
3430
  if (backend == EVBACKEND_KQUEUE ) kqueue_destroy (EV_A);
3058
3431
  #endif
3432
+ #if EV_USE_IOURING
3433
+ if (backend == EVBACKEND_IOURING ) iouring_destroy (EV_A);
3434
+ #endif
3059
3435
  #if EV_USE_LINUXAIO
3060
3436
  if (backend == EVBACKEND_LINUXAIO) linuxaio_destroy (EV_A);
3061
3437
  #endif
@@ -3123,6 +3499,9 @@ loop_fork (EV_P)
3123
3499
  #if EV_USE_KQUEUE
3124
3500
  if (backend == EVBACKEND_KQUEUE ) kqueue_fork (EV_A);
3125
3501
  #endif
3502
+ #if EV_USE_IOURING
3503
+ if (backend == EVBACKEND_IOURING ) iouring_fork (EV_A);
3504
+ #endif
3126
3505
  #if EV_USE_LINUXAIO
3127
3506
  if (backend == EVBACKEND_LINUXAIO) linuxaio_fork (EV_A);
3128
3507
  #endif
@@ -3133,22 +3512,44 @@ loop_fork (EV_P)
3133
3512
  infy_fork (EV_A);
3134
3513
  #endif
3135
3514
 
3136
- #if EV_SIGNAL_ENABLE || EV_ASYNC_ENABLE
3137
- if (ev_is_active (&pipe_w) && postfork != 2)
3515
+ if (postfork != 2)
3138
3516
  {
3139
- /* pipe_write_wanted must be false now, so modifying fd vars should be safe */
3517
+ #if EV_USE_SIGNALFD
3518
+ /* surprisingly, nothing needs to be done for signalfd, accoridng to docs, it does the right thing on fork */
3519
+ #endif
3140
3520
 
3141
- ev_ref (EV_A);
3142
- ev_io_stop (EV_A_ &pipe_w);
3521
+ #if EV_USE_TIMERFD
3522
+ if (ev_is_active (&timerfd_w))
3523
+ {
3524
+ ev_ref (EV_A);
3525
+ ev_io_stop (EV_A_ &timerfd_w);
3143
3526
 
3144
- if (evpipe [0] >= 0)
3145
- EV_WIN32_CLOSE_FD (evpipe [0]);
3527
+ close (timerfd);
3528
+ timerfd = -2;
3529
+
3530
+ evtimerfd_init (EV_A);
3531
+ /* reschedule periodics, in case we missed something */
3532
+ ev_feed_event (EV_A_ &timerfd_w, EV_CUSTOM);
3533
+ }
3534
+ #endif
3146
3535
 
3147
- evpipe_init (EV_A);
3148
- /* iterate over everything, in case we missed something before */
3149
- ev_feed_event (EV_A_ &pipe_w, EV_CUSTOM);
3536
+ #if EV_SIGNAL_ENABLE || EV_ASYNC_ENABLE
3537
+ if (ev_is_active (&pipe_w))
3538
+ {
3539
+ /* pipe_write_wanted must be false now, so modifying fd vars should be safe */
3540
+
3541
+ ev_ref (EV_A);
3542
+ ev_io_stop (EV_A_ &pipe_w);
3543
+
3544
+ if (evpipe [0] >= 0)
3545
+ EV_WIN32_CLOSE_FD (evpipe [0]);
3546
+
3547
+ evpipe_init (EV_A);
3548
+ /* iterate over everything, in case we missed something before */
3549
+ ev_feed_event (EV_A_ &pipe_w, EV_CUSTOM);
3550
+ }
3551
+ #endif
3150
3552
  }
3151
- #endif
3152
3553
 
3153
3554
  postfork = 0;
3154
3555
  }
@@ -3174,7 +3575,7 @@ ev_loop_new (unsigned int flags) EV_NOEXCEPT
3174
3575
  #endif /* multiplicity */
3175
3576
 
3176
3577
  #if EV_VERIFY
3177
- noinline ecb_cold
3578
+ ecb_noinline ecb_cold
3178
3579
  static void
3179
3580
  verify_watcher (EV_P_ W w)
3180
3581
  {
@@ -3184,7 +3585,7 @@ verify_watcher (EV_P_ W w)
3184
3585
  assert (("libev: pending watcher not on pending queue", pendings [ABSPRI (w)][w->pending - 1].w == w));
3185
3586
  }
3186
3587
 
3187
- noinline ecb_cold
3588
+ ecb_noinline ecb_cold
3188
3589
  static void
3189
3590
  verify_heap (EV_P_ ANHE *heap, int N)
3190
3591
  {
@@ -3200,7 +3601,7 @@ verify_heap (EV_P_ ANHE *heap, int N)
3200
3601
  }
3201
3602
  }
3202
3603
 
3203
- noinline ecb_cold
3604
+ ecb_noinline ecb_cold
3204
3605
  static void
3205
3606
  array_verify (EV_P_ W *ws, int cnt)
3206
3607
  {
@@ -3359,7 +3760,7 @@ ev_pending_count (EV_P) EV_NOEXCEPT
3359
3760
  return count;
3360
3761
  }
3361
3762
 
3362
- noinline
3763
+ ecb_noinline
3363
3764
  void
3364
3765
  ev_invoke_pending (EV_P)
3365
3766
  {
@@ -3388,7 +3789,7 @@ ev_invoke_pending (EV_P)
3388
3789
  inline_size void
3389
3790
  idle_reify (EV_P)
3390
3791
  {
3391
- if (expect_false (idleall))
3792
+ if (ecb_expect_false (idleall))
3392
3793
  {
3393
3794
  int pri;
3394
3795
 
@@ -3428,7 +3829,7 @@ timers_reify (EV_P)
3428
3829
  if (ev_at (w) < mn_now)
3429
3830
  ev_at (w) = mn_now;
3430
3831
 
3431
- assert (("libev: negative ev_timer repeat value found while processing timers", w->repeat > 0.));
3832
+ assert (("libev: negative ev_timer repeat value found while processing timers", w->repeat > EV_TS_CONST (0.)));
3432
3833
 
3433
3834
  ANHE_at_cache (timers [HEAP0]);
3434
3835
  downheap (timers, timercnt, HEAP0);
@@ -3447,7 +3848,7 @@ timers_reify (EV_P)
3447
3848
 
3448
3849
  #if EV_PERIODIC_ENABLE
3449
3850
 
3450
- noinline
3851
+ ecb_noinline
3451
3852
  static void
3452
3853
  periodic_recalc (EV_P_ ev_periodic *w)
3453
3854
  {
@@ -3460,7 +3861,7 @@ periodic_recalc (EV_P_ ev_periodic *w)
3460
3861
  ev_tstamp nat = at + w->interval;
3461
3862
 
3462
3863
  /* when resolution fails us, we use ev_rt_now */
3463
- if (expect_false (nat == at))
3864
+ if (ecb_expect_false (nat == at))
3464
3865
  {
3465
3866
  at = ev_rt_now;
3466
3867
  break;
@@ -3516,7 +3917,7 @@ periodics_reify (EV_P)
3516
3917
 
3517
3918
  /* simply recalculate all periodics */
3518
3919
  /* TODO: maybe ensure that at least one event happens when jumping forward? */
3519
- noinline ecb_cold
3920
+ ecb_noinline ecb_cold
3520
3921
  static void
3521
3922
  periodics_reschedule (EV_P)
3522
3923
  {
@@ -3540,7 +3941,7 @@ periodics_reschedule (EV_P)
3540
3941
  #endif
3541
3942
 
3542
3943
  /* adjust all timers by a given offset */
3543
- noinline ecb_cold
3944
+ ecb_noinline ecb_cold
3544
3945
  static void
3545
3946
  timers_reschedule (EV_P_ ev_tstamp adjust)
3546
3947
  {
@@ -3560,7 +3961,7 @@ inline_speed void
3560
3961
  time_update (EV_P_ ev_tstamp max_block)
3561
3962
  {
3562
3963
  #if EV_USE_MONOTONIC
3563
- if (expect_true (have_monotonic))
3964
+ if (ecb_expect_true (have_monotonic))
3564
3965
  {
3565
3966
  int i;
3566
3967
  ev_tstamp odiff = rtmn_diff;
@@ -3569,7 +3970,7 @@ time_update (EV_P_ ev_tstamp max_block)
3569
3970
 
3570
3971
  /* only fetch the realtime clock every 0.5*MIN_TIMEJUMP seconds */
3571
3972
  /* interpolate in the meantime */
3572
- if (expect_true (mn_now - now_floor < MIN_TIMEJUMP * .5))
3973
+ if (ecb_expect_true (mn_now - now_floor < EV_TS_CONST (MIN_TIMEJUMP * .5)))
3573
3974
  {
3574
3975
  ev_rt_now = rtmn_diff + mn_now;
3575
3976
  return;
@@ -3593,7 +3994,7 @@ time_update (EV_P_ ev_tstamp max_block)
3593
3994
 
3594
3995
  diff = odiff - rtmn_diff;
3595
3996
 
3596
- if (expect_true ((diff < 0. ? -diff : diff) < MIN_TIMEJUMP))
3997
+ if (ecb_expect_true ((diff < EV_TS_CONST (0.) ? -diff : diff) < EV_TS_CONST (MIN_TIMEJUMP)))
3597
3998
  return; /* all is well */
3598
3999
 
3599
4000
  ev_rt_now = ev_time ();
@@ -3612,7 +4013,7 @@ time_update (EV_P_ ev_tstamp max_block)
3612
4013
  {
3613
4014
  ev_rt_now = ev_time ();
3614
4015
 
3615
- if (expect_false (mn_now > ev_rt_now || ev_rt_now > mn_now + max_block + MIN_TIMEJUMP))
4016
+ if (ecb_expect_false (mn_now > ev_rt_now || ev_rt_now > mn_now + max_block + EV_TS_CONST (MIN_TIMEJUMP)))
3616
4017
  {
3617
4018
  /* adjust timers. this is easy, as the offset is the same for all of them */
3618
4019
  timers_reschedule (EV_A_ ev_rt_now - mn_now);
@@ -3666,8 +4067,8 @@ ev_run (EV_P_ int flags)
3666
4067
  #endif
3667
4068
 
3668
4069
  #ifndef _WIN32
3669
- if (expect_false (curpid)) /* penalise the forking check even more */
3670
- if (expect_false (getpid () != curpid))
4070
+ if (ecb_expect_false (curpid)) /* penalise the forking check even more */
4071
+ if (ecb_expect_false (getpid () != curpid))
3671
4072
  {
3672
4073
  curpid = getpid ();
3673
4074
  postfork = 1;
@@ -3676,7 +4077,7 @@ ev_run (EV_P_ int flags)
3676
4077
 
3677
4078
  #if EV_FORK_ENABLE
3678
4079
  /* we might have forked, so queue fork handlers */
3679
- if (expect_false (postfork))
4080
+ if (ecb_expect_false (postfork))
3680
4081
  if (forkcnt)
3681
4082
  {
3682
4083
  queue_events (EV_A_ (W *)forks, forkcnt, EV_FORK);
@@ -3686,18 +4087,18 @@ ev_run (EV_P_ int flags)
3686
4087
 
3687
4088
  #if EV_PREPARE_ENABLE
3688
4089
  /* queue prepare watchers (and execute them) */
3689
- if (expect_false (preparecnt))
4090
+ if (ecb_expect_false (preparecnt))
3690
4091
  {
3691
4092
  queue_events (EV_A_ (W *)prepares, preparecnt, EV_PREPARE);
3692
4093
  EV_INVOKE_PENDING;
3693
4094
  }
3694
4095
  #endif
3695
4096
 
3696
- if (expect_false (loop_done))
4097
+ if (ecb_expect_false (loop_done))
3697
4098
  break;
3698
4099
 
3699
4100
  /* we might have forked, so reify kernel state if necessary */
3700
- if (expect_false (postfork))
4101
+ if (ecb_expect_false (postfork))
3701
4102
  loop_fork (EV_A);
3702
4103
 
3703
4104
  /* update fd-related kernel structures */
@@ -3712,16 +4113,28 @@ ev_run (EV_P_ int flags)
3712
4113
  ev_tstamp prev_mn_now = mn_now;
3713
4114
 
3714
4115
  /* update time to cancel out callback processing overhead */
3715
- time_update (EV_A_ 1e100);
4116
+ time_update (EV_A_ EV_TS_CONST (EV_TSTAMP_HUGE));
3716
4117
 
3717
4118
  /* from now on, we want a pipe-wake-up */
3718
4119
  pipe_write_wanted = 1;
3719
4120
 
3720
4121
  ECB_MEMORY_FENCE; /* make sure pipe_write_wanted is visible before we check for potential skips */
3721
4122
 
3722
- if (expect_true (!(flags & EVRUN_NOWAIT || idleall || !activecnt || pipe_write_skipped)))
4123
+ if (ecb_expect_true (!(flags & EVRUN_NOWAIT || idleall || !activecnt || pipe_write_skipped)))
3723
4124
  {
3724
- waittime = MAX_BLOCKTIME;
4125
+ waittime = EV_TS_CONST (MAX_BLOCKTIME);
4126
+
4127
+ #if EV_USE_TIMERFD
4128
+ /* sleep a lot longer when we can reliably detect timejumps */
4129
+ if (ecb_expect_true (timerfd >= 0))
4130
+ waittime = EV_TS_CONST (MAX_BLOCKTIME2);
4131
+ #endif
4132
+ #if !EV_PERIODIC_ENABLE
4133
+ /* without periodics but with monotonic clock there is no need */
4134
+ /* for any time jump detection, so sleep longer */
4135
+ if (ecb_expect_true (have_monotonic))
4136
+ waittime = EV_TS_CONST (MAX_BLOCKTIME2);
4137
+ #endif
3725
4138
 
3726
4139
  if (timercnt)
3727
4140
  {
@@ -3738,23 +4151,28 @@ ev_run (EV_P_ int flags)
3738
4151
  #endif
3739
4152
 
3740
4153
  /* don't let timeouts decrease the waittime below timeout_blocktime */
3741
- if (expect_false (waittime < timeout_blocktime))
4154
+ if (ecb_expect_false (waittime < timeout_blocktime))
3742
4155
  waittime = timeout_blocktime;
3743
4156
 
3744
- /* at this point, we NEED to wait, so we have to ensure */
3745
- /* to pass a minimum nonzero value to the backend */
3746
- if (expect_false (waittime < backend_mintime))
3747
- waittime = backend_mintime;
4157
+ /* now there are two more special cases left, either we have
4158
+ * already-expired timers, so we should not sleep, or we have timers
4159
+ * that expire very soon, in which case we need to wait for a minimum
4160
+ * amount of time for some event loop backends.
4161
+ */
4162
+ if (ecb_expect_false (waittime < backend_mintime))
4163
+ waittime = waittime <= EV_TS_CONST (0.)
4164
+ ? EV_TS_CONST (0.)
4165
+ : backend_mintime;
3748
4166
 
3749
4167
  /* extra check because io_blocktime is commonly 0 */
3750
- if (expect_false (io_blocktime))
4168
+ if (ecb_expect_false (io_blocktime))
3751
4169
  {
3752
4170
  sleeptime = io_blocktime - (mn_now - prev_mn_now);
3753
4171
 
3754
4172
  if (sleeptime > waittime - backend_mintime)
3755
4173
  sleeptime = waittime - backend_mintime;
3756
4174
 
3757
- if (expect_true (sleeptime > 0.))
4175
+ if (ecb_expect_true (sleeptime > EV_TS_CONST (0.)))
3758
4176
  {
3759
4177
  ev_sleep (sleeptime);
3760
4178
  waittime -= sleeptime;
@@ -3809,10 +4227,7 @@ rb_thread_unsafe_dangerous_crazy_blocking_region_end(...);
3809
4227
 
3810
4228
  poll_args.loop = loop;
3811
4229
  poll_args.waittime = waittime;
3812
-
3813
4230
  rb_thread_call_without_gvl(ev_backend_poll, (void *)&poll_args, RUBY_UBF_IO, 0);
3814
-
3815
- // backend_poll (EV_A_ waittime);
3816
4231
  /*
3817
4232
  ############################# END PATCHERY ############################
3818
4233
  */
@@ -3828,7 +4243,6 @@ rb_thread_unsafe_dangerous_crazy_blocking_region_end(...);
3828
4243
  ev_feed_event (EV_A_ &pipe_w, EV_CUSTOM);
3829
4244
  }
3830
4245
 
3831
-
3832
4246
  /* update ev_rt_now, do magic */
3833
4247
  time_update (EV_A_ waittime + sleeptime);
3834
4248
  }
@@ -3846,13 +4260,13 @@ rb_thread_unsafe_dangerous_crazy_blocking_region_end(...);
3846
4260
 
3847
4261
  #if EV_CHECK_ENABLE
3848
4262
  /* queue check watchers, to be executed first */
3849
- if (expect_false (checkcnt))
4263
+ if (ecb_expect_false (checkcnt))
3850
4264
  queue_events (EV_A_ (W *)checks, checkcnt, EV_CHECK);
3851
4265
  #endif
3852
4266
 
3853
4267
  EV_INVOKE_PENDING;
3854
4268
  }
3855
- while (expect_true (
4269
+ while (ecb_expect_true (
3856
4270
  activecnt
3857
4271
  && !loop_done
3858
4272
  && !(flags & (EVRUN_ONCE | EVRUN_NOWAIT))
@@ -3889,7 +4303,7 @@ ev_unref (EV_P) EV_NOEXCEPT
3889
4303
  void
3890
4304
  ev_now_update (EV_P) EV_NOEXCEPT
3891
4305
  {
3892
- time_update (EV_A_ 1e100);
4306
+ time_update (EV_A_ EV_TSTAMP_HUGE);
3893
4307
  }
3894
4308
 
3895
4309
  void
@@ -3926,7 +4340,7 @@ wlist_del (WL *head, WL elem)
3926
4340
  {
3927
4341
  while (*head)
3928
4342
  {
3929
- if (expect_true (*head == elem))
4343
+ if (ecb_expect_true (*head == elem))
3930
4344
  {
3931
4345
  *head = elem->next;
3932
4346
  break;
@@ -3953,7 +4367,7 @@ ev_clear_pending (EV_P_ void *w) EV_NOEXCEPT
3953
4367
  W w_ = (W)w;
3954
4368
  int pending = w_->pending;
3955
4369
 
3956
- if (expect_true (pending))
4370
+ if (ecb_expect_true (pending))
3957
4371
  {
3958
4372
  ANPENDING *p = pendings [ABSPRI (w_)] + pending - 1;
3959
4373
  p->w = (W)&pending_w;
@@ -3990,13 +4404,13 @@ ev_stop (EV_P_ W w)
3990
4404
 
3991
4405
  /*****************************************************************************/
3992
4406
 
3993
- noinline
4407
+ ecb_noinline
3994
4408
  void
3995
4409
  ev_io_start (EV_P_ ev_io *w) EV_NOEXCEPT
3996
4410
  {
3997
4411
  int fd = w->fd;
3998
4412
 
3999
- if (expect_false (ev_is_active (w)))
4413
+ if (ecb_expect_false (ev_is_active (w)))
4000
4414
  return;
4001
4415
 
4002
4416
  assert (("libev: ev_io_start called with negative fd", fd >= 0));
@@ -4020,12 +4434,12 @@ ev_io_start (EV_P_ ev_io *w) EV_NOEXCEPT
4020
4434
  EV_FREQUENT_CHECK;
4021
4435
  }
4022
4436
 
4023
- noinline
4437
+ ecb_noinline
4024
4438
  void
4025
4439
  ev_io_stop (EV_P_ ev_io *w) EV_NOEXCEPT
4026
4440
  {
4027
4441
  clear_pending (EV_A_ (W)w);
4028
- if (expect_false (!ev_is_active (w)))
4442
+ if (ecb_expect_false (!ev_is_active (w)))
4029
4443
  return;
4030
4444
 
4031
4445
  assert (("libev: ev_io_stop called with illegal fd (must stay constant after start!)", w->fd >= 0 && w->fd < anfdmax));
@@ -4043,11 +4457,11 @@ ev_io_stop (EV_P_ ev_io *w) EV_NOEXCEPT
4043
4457
  EV_FREQUENT_CHECK;
4044
4458
  }
4045
4459
 
4046
- noinline
4460
+ ecb_noinline
4047
4461
  void
4048
4462
  ev_timer_start (EV_P_ ev_timer *w) EV_NOEXCEPT
4049
4463
  {
4050
- if (expect_false (ev_is_active (w)))
4464
+ if (ecb_expect_false (ev_is_active (w)))
4051
4465
  return;
4052
4466
 
4053
4467
  ev_at (w) += mn_now;
@@ -4068,12 +4482,12 @@ ev_timer_start (EV_P_ ev_timer *w) EV_NOEXCEPT
4068
4482
  /*assert (("libev: internal timer heap corruption", timers [ev_active (w)] == (WT)w));*/
4069
4483
  }
4070
4484
 
4071
- noinline
4485
+ ecb_noinline
4072
4486
  void
4073
4487
  ev_timer_stop (EV_P_ ev_timer *w) EV_NOEXCEPT
4074
4488
  {
4075
4489
  clear_pending (EV_A_ (W)w);
4076
- if (expect_false (!ev_is_active (w)))
4490
+ if (ecb_expect_false (!ev_is_active (w)))
4077
4491
  return;
4078
4492
 
4079
4493
  EV_FREQUENT_CHECK;
@@ -4085,7 +4499,7 @@ ev_timer_stop (EV_P_ ev_timer *w) EV_NOEXCEPT
4085
4499
 
4086
4500
  --timercnt;
4087
4501
 
4088
- if (expect_true (active < timercnt + HEAP0))
4502
+ if (ecb_expect_true (active < timercnt + HEAP0))
4089
4503
  {
4090
4504
  timers [active] = timers [timercnt + HEAP0];
4091
4505
  adjustheap (timers, timercnt, active);
@@ -4099,7 +4513,7 @@ ev_timer_stop (EV_P_ ev_timer *w) EV_NOEXCEPT
4099
4513
  EV_FREQUENT_CHECK;
4100
4514
  }
4101
4515
 
4102
- noinline
4516
+ ecb_noinline
4103
4517
  void
4104
4518
  ev_timer_again (EV_P_ ev_timer *w) EV_NOEXCEPT
4105
4519
  {
@@ -4130,17 +4544,22 @@ ev_timer_again (EV_P_ ev_timer *w) EV_NOEXCEPT
4130
4544
  ev_tstamp
4131
4545
  ev_timer_remaining (EV_P_ ev_timer *w) EV_NOEXCEPT
4132
4546
  {
4133
- return ev_at (w) - (ev_is_active (w) ? mn_now : 0.);
4547
+ return ev_at (w) - (ev_is_active (w) ? mn_now : EV_TS_CONST (0.));
4134
4548
  }
4135
4549
 
4136
4550
  #if EV_PERIODIC_ENABLE
4137
- noinline
4551
+ ecb_noinline
4138
4552
  void
4139
4553
  ev_periodic_start (EV_P_ ev_periodic *w) EV_NOEXCEPT
4140
4554
  {
4141
- if (expect_false (ev_is_active (w)))
4555
+ if (ecb_expect_false (ev_is_active (w)))
4142
4556
  return;
4143
4557
 
4558
+ #if EV_USE_TIMERFD
4559
+ if (timerfd == -2)
4560
+ evtimerfd_init (EV_A);
4561
+ #endif
4562
+
4144
4563
  if (w->reschedule_cb)
4145
4564
  ev_at (w) = w->reschedule_cb (w, ev_rt_now);
4146
4565
  else if (w->interval)
@@ -4165,12 +4584,12 @@ ev_periodic_start (EV_P_ ev_periodic *w) EV_NOEXCEPT
4165
4584
  /*assert (("libev: internal periodic heap corruption", ANHE_w (periodics [ev_active (w)]) == (WT)w));*/
4166
4585
  }
4167
4586
 
4168
- noinline
4587
+ ecb_noinline
4169
4588
  void
4170
4589
  ev_periodic_stop (EV_P_ ev_periodic *w) EV_NOEXCEPT
4171
4590
  {
4172
4591
  clear_pending (EV_A_ (W)w);
4173
- if (expect_false (!ev_is_active (w)))
4592
+ if (ecb_expect_false (!ev_is_active (w)))
4174
4593
  return;
4175
4594
 
4176
4595
  EV_FREQUENT_CHECK;
@@ -4182,7 +4601,7 @@ ev_periodic_stop (EV_P_ ev_periodic *w) EV_NOEXCEPT
4182
4601
 
4183
4602
  --periodiccnt;
4184
4603
 
4185
- if (expect_true (active < periodiccnt + HEAP0))
4604
+ if (ecb_expect_true (active < periodiccnt + HEAP0))
4186
4605
  {
4187
4606
  periodics [active] = periodics [periodiccnt + HEAP0];
4188
4607
  adjustheap (periodics, periodiccnt, active);
@@ -4194,7 +4613,7 @@ ev_periodic_stop (EV_P_ ev_periodic *w) EV_NOEXCEPT
4194
4613
  EV_FREQUENT_CHECK;
4195
4614
  }
4196
4615
 
4197
- noinline
4616
+ ecb_noinline
4198
4617
  void
4199
4618
  ev_periodic_again (EV_P_ ev_periodic *w) EV_NOEXCEPT
4200
4619
  {
@@ -4210,11 +4629,11 @@ ev_periodic_again (EV_P_ ev_periodic *w) EV_NOEXCEPT
4210
4629
 
4211
4630
  #if EV_SIGNAL_ENABLE
4212
4631
 
4213
- noinline
4632
+ ecb_noinline
4214
4633
  void
4215
4634
  ev_signal_start (EV_P_ ev_signal *w) EV_NOEXCEPT
4216
4635
  {
4217
- if (expect_false (ev_is_active (w)))
4636
+ if (ecb_expect_false (ev_is_active (w)))
4218
4637
  return;
4219
4638
 
4220
4639
  assert (("libev: ev_signal_start called with illegal signal number", w->signum > 0 && w->signum < EV_NSIG));
@@ -4293,12 +4712,12 @@ ev_signal_start (EV_P_ ev_signal *w) EV_NOEXCEPT
4293
4712
  EV_FREQUENT_CHECK;
4294
4713
  }
4295
4714
 
4296
- noinline
4715
+ ecb_noinline
4297
4716
  void
4298
4717
  ev_signal_stop (EV_P_ ev_signal *w) EV_NOEXCEPT
4299
4718
  {
4300
4719
  clear_pending (EV_A_ (W)w);
4301
- if (expect_false (!ev_is_active (w)))
4720
+ if (ecb_expect_false (!ev_is_active (w)))
4302
4721
  return;
4303
4722
 
4304
4723
  EV_FREQUENT_CHECK;
@@ -4341,7 +4760,7 @@ ev_child_start (EV_P_ ev_child *w) EV_NOEXCEPT
4341
4760
  #if EV_MULTIPLICITY
4342
4761
  assert (("libev: child watchers are only supported in the default loop", loop == ev_default_loop_ptr));
4343
4762
  #endif
4344
- if (expect_false (ev_is_active (w)))
4763
+ if (ecb_expect_false (ev_is_active (w)))
4345
4764
  return;
4346
4765
 
4347
4766
  EV_FREQUENT_CHECK;
@@ -4356,7 +4775,7 @@ void
4356
4775
  ev_child_stop (EV_P_ ev_child *w) EV_NOEXCEPT
4357
4776
  {
4358
4777
  clear_pending (EV_A_ (W)w);
4359
- if (expect_false (!ev_is_active (w)))
4778
+ if (ecb_expect_false (!ev_is_active (w)))
4360
4779
  return;
4361
4780
 
4362
4781
  EV_FREQUENT_CHECK;
@@ -4380,14 +4799,14 @@ ev_child_stop (EV_P_ ev_child *w) EV_NOEXCEPT
4380
4799
  #define NFS_STAT_INTERVAL 30.1074891 /* for filesystems potentially failing inotify */
4381
4800
  #define MIN_STAT_INTERVAL 0.1074891
4382
4801
 
4383
- noinline static void stat_timer_cb (EV_P_ ev_timer *w_, int revents);
4802
+ ecb_noinline static void stat_timer_cb (EV_P_ ev_timer *w_, int revents);
4384
4803
 
4385
4804
  #if EV_USE_INOTIFY
4386
4805
 
4387
4806
  /* the * 2 is to allow for alignment padding, which for some reason is >> 8 */
4388
4807
  # define EV_INOTIFY_BUFSIZE (sizeof (struct inotify_event) * 2 + NAME_MAX)
4389
4808
 
4390
- noinline
4809
+ ecb_noinline
4391
4810
  static void
4392
4811
  infy_add (EV_P_ ev_stat *w)
4393
4812
  {
@@ -4462,7 +4881,7 @@ infy_add (EV_P_ ev_stat *w)
4462
4881
  if (ev_is_active (&w->timer)) ev_unref (EV_A);
4463
4882
  }
4464
4883
 
4465
- noinline
4884
+ ecb_noinline
4466
4885
  static void
4467
4886
  infy_del (EV_P_ ev_stat *w)
4468
4887
  {
@@ -4480,7 +4899,7 @@ infy_del (EV_P_ ev_stat *w)
4480
4899
  inotify_rm_watch (fs_fd, wd);
4481
4900
  }
4482
4901
 
4483
- noinline
4902
+ ecb_noinline
4484
4903
  static void
4485
4904
  infy_wd (EV_P_ int slot, int wd, struct inotify_event *ev)
4486
4905
  {
@@ -4636,7 +5055,7 @@ ev_stat_stat (EV_P_ ev_stat *w) EV_NOEXCEPT
4636
5055
  w->attr.st_nlink = 1;
4637
5056
  }
4638
5057
 
4639
- noinline
5058
+ ecb_noinline
4640
5059
  static void
4641
5060
  stat_timer_cb (EV_P_ ev_timer *w_, int revents)
4642
5061
  {
@@ -4680,7 +5099,7 @@ stat_timer_cb (EV_P_ ev_timer *w_, int revents)
4680
5099
  void
4681
5100
  ev_stat_start (EV_P_ ev_stat *w) EV_NOEXCEPT
4682
5101
  {
4683
- if (expect_false (ev_is_active (w)))
5102
+ if (ecb_expect_false (ev_is_active (w)))
4684
5103
  return;
4685
5104
 
4686
5105
  ev_stat_stat (EV_A_ w);
@@ -4712,7 +5131,7 @@ void
4712
5131
  ev_stat_stop (EV_P_ ev_stat *w) EV_NOEXCEPT
4713
5132
  {
4714
5133
  clear_pending (EV_A_ (W)w);
4715
- if (expect_false (!ev_is_active (w)))
5134
+ if (ecb_expect_false (!ev_is_active (w)))
4716
5135
  return;
4717
5136
 
4718
5137
  EV_FREQUENT_CHECK;
@@ -4737,7 +5156,7 @@ ev_stat_stop (EV_P_ ev_stat *w) EV_NOEXCEPT
4737
5156
  void
4738
5157
  ev_idle_start (EV_P_ ev_idle *w) EV_NOEXCEPT
4739
5158
  {
4740
- if (expect_false (ev_is_active (w)))
5159
+ if (ecb_expect_false (ev_is_active (w)))
4741
5160
  return;
4742
5161
 
4743
5162
  pri_adjust (EV_A_ (W)w);
@@ -4761,7 +5180,7 @@ void
4761
5180
  ev_idle_stop (EV_P_ ev_idle *w) EV_NOEXCEPT
4762
5181
  {
4763
5182
  clear_pending (EV_A_ (W)w);
4764
- if (expect_false (!ev_is_active (w)))
5183
+ if (ecb_expect_false (!ev_is_active (w)))
4765
5184
  return;
4766
5185
 
4767
5186
  EV_FREQUENT_CHECK;
@@ -4784,7 +5203,7 @@ ev_idle_stop (EV_P_ ev_idle *w) EV_NOEXCEPT
4784
5203
  void
4785
5204
  ev_prepare_start (EV_P_ ev_prepare *w) EV_NOEXCEPT
4786
5205
  {
4787
- if (expect_false (ev_is_active (w)))
5206
+ if (ecb_expect_false (ev_is_active (w)))
4788
5207
  return;
4789
5208
 
4790
5209
  EV_FREQUENT_CHECK;
@@ -4800,7 +5219,7 @@ void
4800
5219
  ev_prepare_stop (EV_P_ ev_prepare *w) EV_NOEXCEPT
4801
5220
  {
4802
5221
  clear_pending (EV_A_ (W)w);
4803
- if (expect_false (!ev_is_active (w)))
5222
+ if (ecb_expect_false (!ev_is_active (w)))
4804
5223
  return;
4805
5224
 
4806
5225
  EV_FREQUENT_CHECK;
@@ -4822,7 +5241,7 @@ ev_prepare_stop (EV_P_ ev_prepare *w) EV_NOEXCEPT
4822
5241
  void
4823
5242
  ev_check_start (EV_P_ ev_check *w) EV_NOEXCEPT
4824
5243
  {
4825
- if (expect_false (ev_is_active (w)))
5244
+ if (ecb_expect_false (ev_is_active (w)))
4826
5245
  return;
4827
5246
 
4828
5247
  EV_FREQUENT_CHECK;
@@ -4838,7 +5257,7 @@ void
4838
5257
  ev_check_stop (EV_P_ ev_check *w) EV_NOEXCEPT
4839
5258
  {
4840
5259
  clear_pending (EV_A_ (W)w);
4841
- if (expect_false (!ev_is_active (w)))
5260
+ if (ecb_expect_false (!ev_is_active (w)))
4842
5261
  return;
4843
5262
 
4844
5263
  EV_FREQUENT_CHECK;
@@ -4857,7 +5276,7 @@ ev_check_stop (EV_P_ ev_check *w) EV_NOEXCEPT
4857
5276
  #endif
4858
5277
 
4859
5278
  #if EV_EMBED_ENABLE
4860
- noinline
5279
+ ecb_noinline
4861
5280
  void
4862
5281
  ev_embed_sweep (EV_P_ ev_embed *w) EV_NOEXCEPT
4863
5282
  {
@@ -4891,6 +5310,7 @@ embed_prepare_cb (EV_P_ ev_prepare *prepare, int revents)
4891
5310
  }
4892
5311
  }
4893
5312
 
5313
+ #if EV_FORK_ENABLE
4894
5314
  static void
4895
5315
  embed_fork_cb (EV_P_ ev_fork *fork_w, int revents)
4896
5316
  {
@@ -4907,6 +5327,7 @@ embed_fork_cb (EV_P_ ev_fork *fork_w, int revents)
4907
5327
 
4908
5328
  ev_embed_start (EV_A_ w);
4909
5329
  }
5330
+ #endif
4910
5331
 
4911
5332
  #if 0
4912
5333
  static void
@@ -4919,7 +5340,7 @@ embed_idle_cb (EV_P_ ev_idle *idle, int revents)
4919
5340
  void
4920
5341
  ev_embed_start (EV_P_ ev_embed *w) EV_NOEXCEPT
4921
5342
  {
4922
- if (expect_false (ev_is_active (w)))
5343
+ if (ecb_expect_false (ev_is_active (w)))
4923
5344
  return;
4924
5345
 
4925
5346
  {
@@ -4937,8 +5358,10 @@ ev_embed_start (EV_P_ ev_embed *w) EV_NOEXCEPT
4937
5358
  ev_set_priority (&w->prepare, EV_MINPRI);
4938
5359
  ev_prepare_start (EV_A_ &w->prepare);
4939
5360
 
5361
+ #if EV_FORK_ENABLE
4940
5362
  ev_fork_init (&w->fork, embed_fork_cb);
4941
5363
  ev_fork_start (EV_A_ &w->fork);
5364
+ #endif
4942
5365
 
4943
5366
  /*ev_idle_init (&w->idle, e,bed_idle_cb);*/
4944
5367
 
@@ -4951,14 +5374,16 @@ void
4951
5374
  ev_embed_stop (EV_P_ ev_embed *w) EV_NOEXCEPT
4952
5375
  {
4953
5376
  clear_pending (EV_A_ (W)w);
4954
- if (expect_false (!ev_is_active (w)))
5377
+ if (ecb_expect_false (!ev_is_active (w)))
4955
5378
  return;
4956
5379
 
4957
5380
  EV_FREQUENT_CHECK;
4958
5381
 
4959
5382
  ev_io_stop (EV_A_ &w->io);
4960
5383
  ev_prepare_stop (EV_A_ &w->prepare);
5384
+ #if EV_FORK_ENABLE
4961
5385
  ev_fork_stop (EV_A_ &w->fork);
5386
+ #endif
4962
5387
 
4963
5388
  ev_stop (EV_A_ (W)w);
4964
5389
 
@@ -4970,7 +5395,7 @@ ev_embed_stop (EV_P_ ev_embed *w) EV_NOEXCEPT
4970
5395
  void
4971
5396
  ev_fork_start (EV_P_ ev_fork *w) EV_NOEXCEPT
4972
5397
  {
4973
- if (expect_false (ev_is_active (w)))
5398
+ if (ecb_expect_false (ev_is_active (w)))
4974
5399
  return;
4975
5400
 
4976
5401
  EV_FREQUENT_CHECK;
@@ -4986,7 +5411,7 @@ void
4986
5411
  ev_fork_stop (EV_P_ ev_fork *w) EV_NOEXCEPT
4987
5412
  {
4988
5413
  clear_pending (EV_A_ (W)w);
4989
- if (expect_false (!ev_is_active (w)))
5414
+ if (ecb_expect_false (!ev_is_active (w)))
4990
5415
  return;
4991
5416
 
4992
5417
  EV_FREQUENT_CHECK;
@@ -5008,7 +5433,7 @@ ev_fork_stop (EV_P_ ev_fork *w) EV_NOEXCEPT
5008
5433
  void
5009
5434
  ev_cleanup_start (EV_P_ ev_cleanup *w) EV_NOEXCEPT
5010
5435
  {
5011
- if (expect_false (ev_is_active (w)))
5436
+ if (ecb_expect_false (ev_is_active (w)))
5012
5437
  return;
5013
5438
 
5014
5439
  EV_FREQUENT_CHECK;
@@ -5026,7 +5451,7 @@ void
5026
5451
  ev_cleanup_stop (EV_P_ ev_cleanup *w) EV_NOEXCEPT
5027
5452
  {
5028
5453
  clear_pending (EV_A_ (W)w);
5029
- if (expect_false (!ev_is_active (w)))
5454
+ if (ecb_expect_false (!ev_is_active (w)))
5030
5455
  return;
5031
5456
 
5032
5457
  EV_FREQUENT_CHECK;
@@ -5049,7 +5474,7 @@ ev_cleanup_stop (EV_P_ ev_cleanup *w) EV_NOEXCEPT
5049
5474
  void
5050
5475
  ev_async_start (EV_P_ ev_async *w) EV_NOEXCEPT
5051
5476
  {
5052
- if (expect_false (ev_is_active (w)))
5477
+ if (ecb_expect_false (ev_is_active (w)))
5053
5478
  return;
5054
5479
 
5055
5480
  w->sent = 0;
@@ -5069,7 +5494,7 @@ void
5069
5494
  ev_async_stop (EV_P_ ev_async *w) EV_NOEXCEPT
5070
5495
  {
5071
5496
  clear_pending (EV_A_ (W)w);
5072
- if (expect_false (!ev_is_active (w)))
5497
+ if (ecb_expect_false (!ev_is_active (w)))
5073
5498
  return;
5074
5499
 
5075
5500
  EV_FREQUENT_CHECK;
@@ -5276,4 +5701,3 @@ ev_walk (EV_P_ int types, void (*cb)(EV_P_ int type, void *w)) EV_NOEXCEPT
5276
5701
  #if EV_MULTIPLICITY
5277
5702
  #include "ev_wrap.h"
5278
5703
  #endif
5279
-