diff options
-rw-r--r-- | ev.c | 117 | ||||
-rw-r--r-- | ev.pod | 42 | ||||
-rw-r--r-- | ev_vars.h | 2 | ||||
-rw-r--r-- | ev_wrap.h | 4 |
4 files changed, 99 insertions, 66 deletions
@@ -1370,27 +1370,36 @@ evpipe_write (EV_P_ EV_ATOMIC_T *flag) { if (!*flag) { - int old_errno = errno; /* save errno because write might clobber it */ - char dummy; - *flag = 1; -#if EV_USE_EVENTFD - if (evfd >= 0) + pipe_write_skipped = 1; + + if (pipe_write_wanted) { - uint64_t counter = 1; - write (evfd, &counter, sizeof (uint64_t)); - } - else + int old_errno = errno; /* save errno because write will clobber it */ + char dummy; + + pipe_write_skipped = 0; + +#if EV_USE_EVENTFD + if (evfd >= 0) + { + uint64_t counter = 1; + write (evfd, &counter, sizeof (uint64_t)); + } + else #endif - /* win32 people keep sending patches that change this write() to send() */ - /* and then run away. but send() is wrong, it wants a socket handle on win32 */ - /* so when you think this write should be a send instead, please find out */ - /* where your send() is from - it's definitely not the microsoft send, and */ - /* tell me. thank you. */ - write (evpipe [1], &dummy, 1); + { + /* win32 people keep sending patches that change this write() to send() */ + /* and then run away. but send() is wrong, it wants a socket handle on win32 */ + /* so when you think this write should be a send instead, please find out */ + /* where your send() is from - it's definitely not the microsoft send, and */ + /* tell me. thank you. */ + write (evpipe [1], &dummy, 1); + } - errno = old_errno; + errno = old_errno; + } } } @@ -1401,20 +1410,25 @@ pipecb (EV_P_ ev_io *iow, int revents) { int i; -#if EV_USE_EVENTFD - if (evfd >= 0) + if (revents & EV_READ) { - uint64_t counter; - read (evfd, &counter, sizeof (uint64_t)); - } - else +#if EV_USE_EVENTFD + if (evfd >= 0) + { + uint64_t counter; + read (evfd, &counter, sizeof (uint64_t)); + } + else #endif - { - char dummy; - /* see discussion in evpipe_write when you think this read should be recv in win32 */ - read (evpipe [0], &dummy, 1); + { + char dummy; + /* see discussion in evpipe_write when you think this read should be recv in win32 */ + read (evpipe [0], &dummy, 1); + } } + pipe_write_skipped = 0; + #if EV_SIGNAL_ENABLE if (sig_pending) { @@ -1453,6 +1467,8 @@ ev_feed_signal (int signum) return; #endif + evpipe_init (EV_A); + signals [signum - 1].pending = 1; evpipe_write (EV_A_ &sig_pending); } @@ -1759,27 +1775,29 @@ loop_init (EV_P_ unsigned int flags) && getenv ("LIBEV_FLAGS")) flags = atoi (getenv ("LIBEV_FLAGS")); - ev_rt_now = ev_time (); - mn_now = get_clock (); - now_floor = mn_now; - rtmn_diff = ev_rt_now - mn_now; + ev_rt_now = ev_time (); + mn_now = get_clock (); + now_floor = mn_now; + rtmn_diff = ev_rt_now - mn_now; #if EV_FEATURE_API - invoke_cb = ev_invoke_pending; + invoke_cb = ev_invoke_pending; #endif - io_blocktime = 0.; - timeout_blocktime = 0.; - backend = 0; - backend_fd = -1; - sig_pending = 0; + io_blocktime = 0.; + timeout_blocktime = 0.; + backend = 0; + backend_fd = -1; + sig_pending = 0; #if EV_ASYNC_ENABLE - async_pending = 0; + async_pending = 0; #endif + pipe_write_skipped = 0; + pipe_write_wanted = 0; #if EV_USE_INOTIFY - fs_fd = flags & EVFLAG_NOINOTIFY ? -1 : -2; + fs_fd = flags & EVFLAG_NOINOTIFY ? -1 : -2; #endif #if EV_USE_SIGNALFD - sigfd = flags & EVFLAG_SIGNALFD ? -2 : -1; + sigfd = flags & EVFLAG_SIGNALFD ? -2 : -1; #endif if (!(flags & EVBACKEND_MASK)) @@ -1954,12 +1972,7 @@ loop_fork (EV_P) if (ev_is_active (&pipe_w)) { - /* this "locks" the handlers against writing to the pipe */ - /* while we modify the fd vars */ - sig_pending = 1; -#if EV_ASYNC_ENABLE - async_pending = 1; -#endif + /* pipe_write_wanted must be false now, so modifying fd vars should be safe */ ev_ref (EV_A); ev_io_stop (EV_A_ &pipe_w); @@ -2501,7 +2514,10 @@ ev_run (EV_P_ int flags) /* update time to cancel out callback processing overhead */ time_update (EV_A_ 1e100); - if (expect_true (!(flags & EVRUN_NOWAIT || idleall || !activecnt))) + /* from now on, we want a pipe-wake-up */ + pipe_write_wanted = 1; + + if (expect_true (!(flags & EVRUN_NOWAIT || idleall || !activecnt || pipe_write_skipped))) { waittime = MAX_BLOCKTIME; @@ -2551,6 +2567,15 @@ ev_run (EV_P_ int flags) backend_poll (EV_A_ waittime); assert ((loop_done = EVBREAK_CANCEL, 1)); /* assert for side effect */ + pipe_write_wanted = 0; + + if (pipe_write_skipped) + { + assert (("libev: pipe_w not active, but pipe not written", ev_is_active (&pipe_w))); + ev_feed_event (EV_A_ &pipe_w, EV_CUSTOM); + } + + /* update ev_rt_now, do magic */ time_update (EV_A_ waittime + sleeptime); } @@ -610,11 +610,11 @@ hacks). On the negative side, the interface is I<bizarre> - so bizarre that even sun itself gets it wrong in their code examples: The event polling -function sometimes returning events to the caller even though an error +function sometimes returns events to the caller even though an error occurred, but with no indication whether it has done so or not (yes, it's -even documented that way) - deadly for edge-triggered interfaces where -you absolutely have to know whether an event occurred or not because you -have to re-arm the watcher. +even documented that way) - deadly for edge-triggered interfaces where you +absolutely have to know whether an event occurred or not because you have +to re-arm the watcher. Fortunately libev seems to be able to work around these idiocies. @@ -2025,7 +2025,7 @@ do stuff) the timer will not fire more than once per event loop iteration. =item ev_timer_again (loop, ev_timer *) -This will act as if the timer timed out and restart it again if it is +This will act as if the timer timed out and restarts it again if it is repeating. The exact semantics are: If the timer is pending, its pending status is cleared. @@ -3222,9 +3222,6 @@ of "global async watchers" by using a watcher on an otherwise unused signal, and C<ev_feed_signal> to signal this watcher from another thread, even without knowing which loop owns the signal. -Unlike C<ev_signal> watchers, C<ev_async> works with any event loop, not -just the default loop. - =head3 Queueing C<ev_async> does not support queueing of data in any way. The reason @@ -3333,13 +3330,16 @@ signal or similar contexts (see the discussion of C<EV_ATOMIC_T> in the embedding section below on what exactly this means). Note that, as with other watchers in libev, multiple events might get -compressed into a single callback invocation (another way to look at this -is that C<ev_async> watchers are level-triggered, set on C<ev_async_send>, -reset when the event loop detects that). +compressed into a single callback invocation (another way to look at +this is that C<ev_async> watchers are level-triggered: they are set on +C<ev_async_send>, reset when the event loop detects that). -This call incurs the overhead of a system call only once per event loop -iteration, so while the overhead might be noticeable, it doesn't apply to -repeated calls to C<ev_async_send> for the same event loop. +This call incurs the overhead of at most one extra system call per event +loop iteration, if the event loop is blocked, and no syscall at all if +the event loop (or your program) is processing events. That means that +repeated calls are basically free (there is no need to avoid calls for +performance reasons) and that the overhead becomes smaller (typically +zero) under load. =item bool = ev_async_pending (ev_async *) @@ -4371,10 +4371,11 @@ indicate GNU/Linux + Glibc 2.4 or newer, otherwise disabled. =item EV_ATOMIC_T Libev requires an integer type (suitable for storing C<0> or C<1>) whose -access is atomic with respect to other threads or signal contexts. No such -type is easily found in the C language, so you can provide your own type -that you know is safe for your purposes. It is used both for signal handler "locking" -as well as for signal and thread safety in C<ev_async> watchers. +access is atomic and serialised with respect to other threads or signal +contexts. No such type is easily found in the C language, so you can +provide your own type that you know is safe for your purposes. It is used +both for signal handler "locking" as well as for signal and thread safety +in C<ev_async> watchers. In the absence of this define, libev will use C<sig_atomic_t volatile> (from F<signal.h>), which is usually good enough on most platforms. @@ -5122,8 +5123,9 @@ watchers becomes O(1) with respect to priority handling. =item Processing signals: O(max_signal_number) Sending involves a system call I<iff> there were no other C<ev_async_send> -calls in the current loop iteration. Checking for async and signal events -involves iterating over all running async watchers or all signal numbers. +calls in the current loop iteration and the loop is currently +blocked. Checking for async and signal events involves iterating over all +running async watchers or all signal numbers. =back @@ -73,6 +73,8 @@ VARx(int, evfd) #endif VAR (evpipe, int evpipe [2]) VARx(ev_io, pipe_w) +VARx(EV_ATOMIC_T, pipe_write_wanted) +VARx(EV_ATOMIC_T, pipe_write_skipped) #if !defined(_WIN32) || EV_GENWRAP VARx(pid_t, curpid) @@ -25,6 +25,8 @@ #define evfd ((loop)->evfd) #define evpipe ((loop)->evpipe) #define pipe_w ((loop)->pipe_w) +#define pipe_write_wanted ((loop)->pipe_write_wanted) +#define pipe_write_skipped ((loop)->pipe_write_skipped) #define curpid ((loop)->curpid) #define postfork ((loop)->postfork) #define vec_ri ((loop)->vec_ri) @@ -122,6 +124,8 @@ #undef evfd #undef evpipe #undef pipe_w +#undef pipe_write_wanted +#undef pipe_write_skipped #undef curpid #undef postfork #undef vec_ri |