]> rtime.felk.cvut.cz Git - sojka/libev.git/commitdiff
*** empty log message ***
authorMarc Alexander Lehmann <libev@schmorp.de>
Sat, 22 Dec 2007 05:47:56 +0000 (05:47 +0000)
committerMarc Alexander Lehmann <libev@schmorp.de>
Sat, 22 Dec 2007 05:47:56 +0000 (05:47 +0000)
Symbols.ev
ev.3
ev.c
ev.h
ev.pod
ev_vars.h
ev_wrap.h
libev.m4

index 501722bd83bb228a894cdfab06f0f9871e58b116..d95926a7691185b9b8bc0906422dd3f131860b77 100644 (file)
@@ -37,9 +37,12 @@ ev_prepare_stop
 ev_recommended_backends
 ev_ref
 ev_set_allocator
+ev_set_io_collect_interval
 ev_set_syserr_cb
+ev_set_timeout_collect_interval
 ev_signal_start
 ev_signal_stop
+ev_sleep
 ev_stat_start
 ev_stat_stat
 ev_stat_stop
diff --git a/ev.3 b/ev.3
index 371d846e805897be9f2927fbcb26dcdc6341c0e3..7689fc0db0ef47b874ed4be15edbfbb54620ddb5 100644 (file)
--- a/ev.3
+++ b/ev.3
 .\" ========================================================================
 .\"
 .IX Title "EV 1"
-.TH EV 1 "2007-12-21" "perl v5.8.8" "User Contributed Perl Documentation"
+.TH EV 1 "2007-12-22" "perl v5.8.8" "User Contributed Perl Documentation"
 .SH "NAME"
 libev \- a high performance full\-featured event loop written in C
 .SH "SYNOPSIS"
@@ -257,6 +257,11 @@ library in any way.
 Returns the current time as libev would use it. Please note that the
 \&\f(CW\*(C`ev_now\*(C'\fR function is usually faster and also often returns the timestamp
 you actually want to know.
+.IP "void ev_sleep (ev_tstamp interval)" 4
+.IX Item "void ev_sleep (ev_tstamp interval)"
+Sleep for the given interval: The current thread will be blocked until
+either it is interrupted or the given time interval has passed. Basically
+this is a subsecond-resolution \f(CW\*(C`sleep ()\*(C'\fR.
 .IP "int ev_version_major ()" 4
 .IX Item "int ev_version_major ()"
 .PD 0
@@ -465,7 +470,7 @@ but it scales phenomenally better. While poll and select usually scale
 like O(total_fds) where n is the total number of fds (or the highest fd),
 epoll scales either O(1) or O(active_fds). The epoll design has a number
 of shortcomings, such as silently dropping events in some hard-to-detect
-cases and rewuiring a syscall per fd change, no fork support and bad
+cases and rewiring a syscall per fd change, no fork support and bad
 support for dup:
 .Sp
 While stopping, setting and starting an I/O watcher in the same iteration
@@ -726,6 +731,41 @@ Example: For some weird reason, unregister the above signal handler again.
 \&  ev_ref (loop);
 \&  ev_signal_stop (loop, &exitsig);
 .Ve
+.IP "ev_set_io_collect_interval (ev_tstamp interval)" 4
+.IX Item "ev_set_io_collect_interval (ev_tstamp interval)"
+.PD 0
+.IP "ev_set_timeout_collect_interval (ev_tstamp interval)" 4
+.IX Item "ev_set_timeout_collect_interval (ev_tstamp interval)"
+.PD
+These advanced functions influence the time that libev will spend waiting
+for events. Both are by default \f(CW0\fR, meaning that libev will try to
+invoke timer/periodic callbacks and I/O callbacks with minimum latency.
+.Sp
+Setting these to a higher value (the \f(CW\*(C`interval\*(C'\fR \fImust\fR be >= \f(CW0\fR)
+allows libev to delay invocation of I/O and timer/periodic callbacks to
+increase efficiency of loop iterations.
+.Sp
+The background is that sometimes your program runs just fast enough to
+handle one (or very few) event(s) per loop iteration. While this makes
+the program responsive, it also wastes a lot of \s-1CPU\s0 time to poll for new
+events, especially with backends like \f(CW\*(C`select ()\*(C'\fR which have a high
+overhead for the actual polling but can deliver many events at once.
+.Sp
+By setting a higher \fIio collect interval\fR you allow libev to spend more
+time collecting I/O events, so you can handle more events per iteration,
+at the cost of increasing latency. Timeouts (both \f(CW\*(C`ev_periodic\*(C'\fR and
+\&\f(CW\*(C`ev_timer\*(C'\fR) will be not affected.
+.Sp
+Likewise, by setting a higher \fItimeout collect interval\fR you allow libev
+to spend more time collecting timeouts, at the expense of increased
+latency (the watcher callback will be called later). \f(CW\*(C`ev_io\*(C'\fR watchers
+will not be affected.
+.Sp
+Many programs can usually benefit by setting the io collect interval to
+a value near \f(CW0.1\fR or so, which is often enough for interactive servers
+(of course not for games), likewise for timeouts. It usually doesn't make
+much sense to set it to a lower value than \f(CW0.01\fR, as this approsaches
+the timing granularity of most systems.
 .SH "ANATOMY OF A WATCHER"
 .IX Header "ANATOMY OF A WATCHER"
 A watcher is a structure that you create and register to record your
@@ -2503,6 +2543,10 @@ runtime if successful). Otherwise no use of the realtime clock option will
 be attempted. This effectively replaces \f(CW\*(C`gettimeofday\*(C'\fR by \f(CW\*(C`clock_get
 (CLOCK_REALTIME, ...)\*(C'\fR and will not normally affect correctness. See the
 note about libraries in the description of \f(CW\*(C`EV_USE_MONOTONIC\*(C'\fR, though.
+.IP "\s-1EV_USE_NANOSLEEP\s0" 4
+.IX Item "EV_USE_NANOSLEEP"
+If defined to be \f(CW1\fR, libev will assume that \f(CW\*(C`nanosleep ()\*(C'\fR is available
+and will use it for delays. Otherwise it will use \f(CW\*(C`select ()\*(C'\fR.
 .IP "\s-1EV_USE_SELECT\s0" 4
 .IX Item "EV_USE_SELECT"
 If undefined or defined to be \f(CW1\fR, libev will compile in support for the
diff --git a/ev.c b/ev.c
index 3f9fe2bdd5c7f055c5ebe850f9a7ed700d6d1b03..14213df8f039858fdcd18a9e79d0ff779d127b70 100644 (file)
--- a/ev.c
+++ b/ev.c
@@ -56,6 +56,14 @@ extern "C" {
 #  endif
 # endif
 
+# ifndef EV_USE_NANOSLEEP
+#  if HAVE_NANOSLEEP
+#   define EV_USE_NANOSLEEP 1
+#  else
+#   define EV_USE_NANOSLEEP 0
+#  endif
+# endif
+
 # ifndef EV_USE_SELECT
 #  if HAVE_SELECT && HAVE_SYS_SELECT_H
 #   define EV_USE_SELECT 1
@@ -148,6 +156,10 @@ extern "C" {
 # define EV_USE_REALTIME 0
 #endif
 
+#ifndef EV_USE_NANOSLEEP
+# define EV_USE_NANOSLEEP 0
+#endif
+
 #ifndef EV_USE_SELECT
 # define EV_USE_SELECT 1
 #endif
@@ -209,6 +221,12 @@ extern "C" {
 # define EV_USE_INOTIFY 0
 #endif
 
+#if !EV_USE_NANOSLEEP
+# ifndef _WIN32
+#  include <sys/select.h>
+# endif
+#endif
+
 #if EV_USE_INOTIFY
 # include <sys/inotify.h>
 #endif
@@ -410,6 +428,33 @@ ev_now (EV_P)
 }
 #endif
 
+void
+ev_sleep (ev_tstamp delay)
+{
+  if (delay > 0.)
+    {
+#if EV_USE_NANOSLEEP
+      struct timespec ts;
+
+      ts.tv_sec  = (time_t)delay;
+      ts.tv_nsec = (long)((delay - (ev_tstamp)(ts.tv_sec)) * 1e9);
+
+      nanosleep (&ts, 0);
+#elif defined(_WIN32)
+      Sleep (delay * 1e3);
+#else
+      struct timeval tv;
+
+      tv.tv_sec  = (time_t)delay;
+      tv.tv_usec = (long)((delay - (ev_tstamp)(tv.tv_sec)) * 1e6);
+
+      select (0, 0, 0, 0, &tv);
+#endif
+    }
+}
+
+/*****************************************************************************/
+
 int inline_size
 array_nextsize (int elem, int cur, int cnt)
 {
@@ -944,6 +989,18 @@ ev_loop_count (EV_P)
   return loop_count;
 }
 
+void
+ev_set_io_collect_interval (EV_P_ ev_tstamp interval)
+{
+  io_blocktime = interval;
+}
+
+void
+ev_set_timeout_collect_interval (EV_P_ ev_tstamp interval)
+{
+  timeout_blocktime = interval;
+}
+
 static void noinline
 loop_init (EV_P_ unsigned int flags)
 {
@@ -962,6 +1019,9 @@ loop_init (EV_P_ unsigned int flags)
       now_floor = mn_now;
       rtmn_diff = ev_rt_now - mn_now;
 
+      io_blocktime      = 0.;
+      timeout_blocktime = 0.;
+
       /* pid check not overridable via env */
 #ifndef _WIN32
       if (flags & EVFLAG_FORKCHECK)
@@ -1458,39 +1518,50 @@ ev_loop (EV_P_ int flags)
 
       /* calculate blocking time */
       {
-        ev_tstamp block;
+        ev_tstamp waittime  = 0.;
+        ev_tstamp sleeptime = 0.;
 
-        if (expect_false (flags & EVLOOP_NONBLOCK || idleall || !activecnt))
-          block = 0.; /* do not block at all */
-        else
+        if (expect_true (!(flags & EVLOOP_NONBLOCK || idleall || !activecnt)))
           {
             /* update time to cancel out callback processing overhead */
             time_update (EV_A_ 1e100);
 
-            block = MAX_BLOCKTIME;
+            waittime = MAX_BLOCKTIME;
 
             if (timercnt)
               {
                 ev_tstamp to = ((WT)timers [0])->at - mn_now + backend_fudge;
-                if (block > to) block = to;
+                if (waittime > to) waittime = to;
               }
 
 #if EV_PERIODIC_ENABLE
             if (periodiccnt)
               {
                 ev_tstamp to = ((WT)periodics [0])->at - ev_rt_now + backend_fudge;
-                if (block > to) block = to;
+                if (waittime > to) waittime = to;
               }
 #endif
 
-            if (expect_false (block < 0.)) block = 0.;
+            if (expect_false (waittime < timeout_blocktime))
+              waittime = timeout_blocktime;
+
+            sleeptime = waittime - backend_fudge;
+
+            if (expect_true (sleeptime > io_blocktime))
+              sleeptime = io_blocktime;
+
+            if (sleeptime)
+              {
+                ev_sleep (sleeptime);
+                waittime -= sleeptime;
+              }
           }
 
         ++loop_count;
-        backend_poll (EV_A_ block);
+        backend_poll (EV_A_ waittime);
 
         /* update ev_rt_now, do magic */
-        time_update (EV_A_ block);
+        time_update (EV_A_ waittime + sleeptime);
       }
 
       /* queue pending timers and reschedule them */
diff --git a/ev.h b/ev.h
index 2b59019887afa7505814fe7bb33a8a263d19a916..f28d981ae4c9424788c2627ef7d709afb40f3f01 100644 (file)
--- a/ev.h
+++ b/ev.h
@@ -346,6 +346,7 @@ unsigned int ev_recommended_backends (void);
 unsigned int ev_embeddable_backends (void);
 
 ev_tstamp ev_time (void);
+void ev_sleep (ev_tstamp delay); /* sleep for a while */
 
 /* Sets the allocation function to use, works like realloc.
  * It is used to allocate and free memory.
@@ -403,8 +404,8 @@ void ev_default_destroy (void); /* destroy the default loop */
 /* you can actually call it at any time, anywhere :) */
 void ev_default_fork (void);
 
-unsigned int ev_backend (EV_P);
-unsigned int ev_loop_count (EV_P);
+unsigned int ev_backend (EV_P);    /* backend in use by loop */
+unsigned int ev_loop_count (EV_P); /* number of loop iterations */
 #endif
 
 #define EVLOOP_NONBLOCK        1 /* do not block/wait */
@@ -417,6 +418,9 @@ unsigned int ev_loop_count (EV_P);
 void ev_loop (EV_P_ int flags);
 void ev_unloop (EV_P_ int how); /* set to 1 to break out of event loop, set to 2 to break out of all event loops */
 
+void ev_set_io_collect_interval (EV_P_ ev_tstamp interval); /* sleep at least this time, default 0 */
+void ev_set_timeout_collect_interval (EV_P_ ev_tstamp interval); /* sleep at least this time, default 0 */
+
 /*
  * ref/unref can be used to add or remove a refcount on the mainloop. every watcher
  * keeps one reference. if you have a long-runing watcher you never unregister that
diff --git a/ev.pod b/ev.pod
index 1a53dca6587583a4ee3d9de4c86f0591de016cdf..d1d83489e6b0ef315c676d4e0ad178047a0c7a11 100644 (file)
--- a/ev.pod
+++ b/ev.pod
@@ -117,6 +117,12 @@ Returns the current time as libev would use it. Please note that the
 C<ev_now> function is usually faster and also often returns the timestamp
 you actually want to know.
 
+=item ev_sleep (ev_tstamp interval)
+
+Sleep for the given interval: The current thread will be blocked until
+either it is interrupted or the given time interval has passed. Basically
+this is a subsecond-resolution C<sleep ()>.
+
 =item int ev_version_major ()
 
 =item int ev_version_minor ()
@@ -571,6 +577,40 @@ Example: For some weird reason, unregister the above signal handler again.
   ev_ref (loop);
   ev_signal_stop (loop, &exitsig);
 
+=item ev_set_io_collect_interval (loop, ev_tstamp interval)
+
+=item ev_set_timeout_collect_interval (loop, ev_tstamp interval)
+
+These advanced functions influence the time that libev will spend waiting
+for events. Both are by default C<0>, meaning that libev will try to
+invoke timer/periodic callbacks and I/O callbacks with minimum latency.
+
+Setting these to a higher value (the C<interval> I<must> be >= C<0>)
+allows libev to delay invocation of I/O and timer/periodic callbacks to
+increase efficiency of loop iterations.
+
+The background is that sometimes your program runs just fast enough to
+handle one (or very few) event(s) per loop iteration. While this makes
+the program responsive, it also wastes a lot of CPU time to poll for new
+events, especially with backends like C<select ()> which have a high
+overhead for the actual polling but can deliver many events at once.
+
+By setting a higher I<io collect interval> you allow libev to spend more
+time collecting I/O events, so you can handle more events per iteration,
+at the cost of increasing latency. Timeouts (both C<ev_periodic> and
+C<ev_timer>) will be not affected.
+
+Likewise, by setting a higher I<timeout collect interval> you allow libev
+to spend more time collecting timeouts, at the expense of increased
+latency (the watcher callback will be called later). C<ev_io> watchers
+will not be affected.
+
+Many programs can usually benefit by setting the io collect interval to
+a value near C<0.1> or so, which is often enough for interactive servers
+(of course not for games), likewise for timeouts. It usually doesn't make
+much sense to set it to a lower value than C<0.01>, as this approsaches
+the timing granularity of most systems.
+
 =back
 
 
@@ -2299,6 +2339,11 @@ be attempted. This effectively replaces C<gettimeofday> by C<clock_get
 (CLOCK_REALTIME, ...)> and will not normally affect correctness. See the
 note about libraries in the description of C<EV_USE_MONOTONIC>, though.
 
+=item EV_USE_NANOSLEEP
+
+If defined to be C<1>, libev will assume that C<nanosleep ()> is available
+and will use it for delays. Otherwise it will use C<select ()>.
+
 =item EV_USE_SELECT
 
 If undefined or defined to be C<1>, libev will compile in support for the
index 36e748524cc0c05927e3b70f6881e414dfe4243f..d653213ee401ece5dfc92d49a9e642a17bcaf59c 100644 (file)
--- a/ev_vars.h
+++ b/ev_vars.h
@@ -3,14 +3,18 @@
 VARx(ev_tstamp, now_floor) /* last time we refreshed rt_time */
 VARx(ev_tstamp, mn_now)    /* monotonic clock "now" */
 VARx(ev_tstamp, rtmn_diff) /* difference realtime - monotonic time */
+
+VARx(ev_tstamp, io_blocktime);
+VARx(ev_tstamp, timeout_blocktime);
+
 VARx(int, backend)
 VARx(int, activecnt) /* total number of active events ("refcount") */
 VARx(unsigned int, loop_count); /* total number of loop iterations/blocks */
 
+VARx(int, backend_fd)
 VARx(ev_tstamp, backend_fudge) /* assumed typical timer resolution */
 VAR (backend_modify, void (*backend_modify)(EV_P_ int fd, int oev, int nev))
 VAR (backend_poll  , void (*backend_poll)(EV_P_ ev_tstamp timeout))
-VARx(int, backend_fd)
 
 #if !defined(_WIN32) || EV_GENWRAP
 VARx(pid_t, curpid)
index ba028aed1bd8699a4b13e403bda5758277c84591..f9bc61e75d0f0697aad9b60104a7622cd90e3631 100644 (file)
--- a/ev_wrap.h
+++ b/ev_wrap.h
@@ -4,13 +4,15 @@
 #define now_floor ((loop)->now_floor)
 #define mn_now ((loop)->mn_now)
 #define rtmn_diff ((loop)->rtmn_diff)
+#define io_blocktime ((loop)->io_blocktime)
+#define timeout_blocktime ((loop)->timeout_blocktime)
 #define backend ((loop)->backend)
 #define activecnt ((loop)->activecnt)
 #define loop_count ((loop)->loop_count)
+#define backend_fd ((loop)->backend_fd)
 #define backend_fudge ((loop)->backend_fudge)
 #define backend_modify ((loop)->backend_modify)
 #define backend_poll ((loop)->backend_poll)
-#define backend_fd ((loop)->backend_fd)
 #define curpid ((loop)->curpid)
 #define postfork ((loop)->postfork)
 #define vec_ri ((loop)->vec_ri)
 #undef now_floor
 #undef mn_now
 #undef rtmn_diff
+#undef io_blocktime
+#undef timeout_blocktime
 #undef backend
 #undef activecnt
 #undef loop_count
+#undef backend_fd
 #undef backend_fudge
 #undef backend_modify
 #undef backend_poll
-#undef backend_fd
 #undef curpid
 #undef postfork
 #undef vec_ri
index 322b659bead949672d3fe6abb8f7432b86cdfe73..8099d3b64809c93cdb5ffa6b83beda02be61737e 100644 (file)
--- a/libev.m4
+++ b/libev.m4
@@ -14,6 +14,14 @@ AC_CHECK_FUNC(clock_gettime, [], [
    fi
 ])
 
+AC_CHECK_FUNC(nanonsleep, [], [ 
+   if test -z "$LIBEV_M4_AVOID_LIBRT"; then
+      AC_CHECK_LIB(rt, nanonsleep) 
+      unset ac_cv_func_nanonsleep
+      AC_CHECK_FUNCS(nanonsleep)
+   fi
+])
+
 AC_CHECK_LIB(m, ceil)