diff --git a/docs/c-api/mg_net.h/mg_mgr_free.md b/docs/c-api/mg_net.h/mg_mgr_free.md
index d2df34e4c268278da83abc9d4c2b725aea297943..60ad75bdc512b4dbf8ca0e1debf5b92cad1e97d0 100644
--- a/docs/c-api/mg_net.h/mg_mgr_free.md
+++ b/docs/c-api/mg_net.h/mg_mgr_free.md
@@ -3,7 +3,7 @@ title: "mg_mgr_free()"
 decl_name: "mg_mgr_free"
 symbol_kind: "func"
 signature: |
-  void mg_mgr_free(struct mg_mgr *);
+  void mg_mgr_free(struct mg_mgr *mgr);
 ---
 
 De-initialises Mongoose manager.
diff --git a/docs/c-api/mg_net.h/mg_mgr_poll.md b/docs/c-api/mg_net.h/mg_mgr_poll.md
index b70c31b6db0229534694d1a8e31a8328a2e2f723..ef48a190826f11664f3d703b95ba72368ad16a8b 100644
--- a/docs/c-api/mg_net.h/mg_mgr_poll.md
+++ b/docs/c-api/mg_net.h/mg_mgr_poll.md
@@ -3,11 +3,11 @@ title: "mg_mgr_poll()"
 decl_name: "mg_mgr_poll"
 symbol_kind: "func"
 signature: |
-  time_t mg_mgr_poll(struct mg_mgr *, int milli);
+  int mg_mgr_poll(struct mg_mgr *mgr, int milli);
 ---
 
 This function performs the actual IO and must be called in a loop
-(an event loop). It returns the current timestamp.
+(an event loop). It returns number of user events generated (except POLLs).
 `milli` is the maximum number of milliseconds to sleep.
 `mg_mgr_poll()` checks all connections for IO readiness. If at least one
 of the connections is IO-ready, `mg_mgr_poll()` triggers the respective
diff --git a/docs/c-api/mg_net.h/struct_mg_mgr.md b/docs/c-api/mg_net.h/struct_mg_mgr.md
index d33727c38d2314ec252096ed8a560d0a59b02f4e..44efa5fdf21f07fb692da61ecd29132fcc7777a0 100644
--- a/docs/c-api/mg_net.h/struct_mg_mgr.md
+++ b/docs/c-api/mg_net.h/struct_mg_mgr.md
@@ -13,6 +13,7 @@ signature: |
   #endif
     void *user_data; /* User data */
     int num_ifaces;
+    int num_calls;
     struct mg_iface **ifaces; /* network interfaces */
     const char *nameserver;   /* DNS server to use */
   };
diff --git a/mongoose.c b/mongoose.c
index d09345c5ea54514447bc222648d601caad1450a7..7b0170c2b5563b804b2bdf3e16d2b4355a43f960 100644
--- a/mongoose.c
+++ b/mongoose.c
@@ -2408,6 +2408,7 @@ MG_INTERNAL void mg_call(struct mg_connection *nc,
                   (nc->flags & _MG_CALLBACK_MODIFIABLE_FLAGS_MASK);
     }
   }
+  if (ev != MG_EV_POLL) nc->mgr->num_calls++;
   if (ev != MG_EV_POLL) {
     DBG(("%p after %s flags=0x%lx rmbl=%d smbl=%d", nc,
          ev_handler == nc->handler ? "user" : "proto", nc->flags,
@@ -2585,19 +2586,14 @@ void mg_mgr_free(struct mg_mgr *m) {
   MG_FREE((char *) m->nameserver);
 }
 
-time_t mg_mgr_poll(struct mg_mgr *m, int timeout_ms) {
-  int i;
-  time_t now = 0; /* oh GCC, seriously ? */
-
-  if (m->num_ifaces == 0) {
-    LOG(LL_ERROR, ("cannot poll: no interfaces"));
-    return 0;
-  }
+int mg_mgr_poll(struct mg_mgr *m, int timeout_ms) {
+  int i, num_calls_before = m->num_calls;
 
   for (i = 0; i < m->num_ifaces; i++) {
-    now = m->ifaces[i]->vtable->poll(m->ifaces[i], timeout_ms);
+    m->ifaces[i]->vtable->poll(m->ifaces[i], timeout_ms);
   }
-  return now;
+
+  return (m->num_calls - num_calls_before);
 }
 
 int mg_vprintf(struct mg_connection *nc, const char *fmt, va_list ap) {
@@ -3585,6 +3581,18 @@ struct mg_iface *mg_find_iface(struct mg_mgr *mgr,
   }
   return NULL;
 }
+
+double mg_mgr_min_timer(const struct mg_mgr *mgr) {
+  double min_timer = 0;
+  struct mg_connection *nc;
+  for (nc = mgr->active_connections; nc != NULL; nc = nc->next) {
+    if (nc->ev_timer_time <= 0) continue;
+    if (min_timer == 0 || nc->ev_timer_time < min_timer) {
+      min_timer = nc->ev_timer_time;
+    }
+  }
+  return min_timer;
+}
 #ifdef MG_MODULE_LINES
 #line 1 "mongoose/src/mg_net_if_socket.c"
 #endif
@@ -15716,38 +15724,6 @@ time_t mg_lwip_if_poll(struct mg_iface *iface, int timeout_ms) {
   return now;
 }
 
-uint32_t mg_lwip_get_poll_delay_ms(struct mg_mgr *mgr) {
-  struct mg_connection *nc;
-  double now;
-  double min_timer = 0;
-  int num_timers = 0;
-  mg_ev_mgr_lwip_process_signals(mgr);
-  for (nc = mg_next(mgr, NULL); nc != NULL; nc = mg_next(mgr, nc)) {
-    struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock;
-    if (nc->ev_timer_time > 0) {
-      if (num_timers == 0 || nc->ev_timer_time < min_timer) {
-        min_timer = nc->ev_timer_time;
-      }
-      num_timers++;
-    }
-    /* We want and can send data, request a poll immediately. */
-    if (nc->sock != INVALID_SOCKET && mg_lwip_if_can_send(nc, cs)) {
-      return 0;
-    }
-  }
-  uint32_t timeout_ms = ~0;
-  now = mg_time();
-  if (num_timers > 0) {
-    /* If we have a timer that is past due, do a poll ASAP. */
-    if (min_timer < now) return 0;
-    double timer_timeout_ms = (min_timer - now) * 1000 + 1 /* rounding */;
-    if (timer_timeout_ms < timeout_ms) {
-      timeout_ms = timer_timeout_ms;
-    }
-  }
-  return timeout_ms;
-}
-
 #endif /* MG_NET_IF == MG_NET_IF_LWIP_LOW_LEVEL */
 #ifdef MG_MODULE_LINES
 #line 1 "common/platforms/wince/wince_libc.c"
diff --git a/mongoose.h b/mongoose.h
index 2667c75905388b73424764bea8f5a93e63f1fecf..7c179d692fa74ed1faca880f3fed47396312b20e 100644
--- a/mongoose.h
+++ b/mongoose.h
@@ -2030,7 +2030,6 @@ typedef int sock_t;
 #if MG_NET_IF == MG_NET_IF_LWIP_LOW_LEVEL
 struct mg_mgr;
 struct mg_connection;
-uint32_t mg_lwip_get_poll_delay_ms(struct mg_mgr *mgr);
 void mg_lwip_set_keepalive_params(struct mg_connection *nc, int idle,
                                   int interval, int count);
 #endif
@@ -3718,6 +3717,12 @@ void mg_if_recv_udp_cb(struct mg_connection *nc, void *buf, int len,
 /* Deliver a POLL event to the connection. */
 int mg_if_poll(struct mg_connection *nc, double now);
 
+/*
+ * Return minimal timer value amoung connections in the manager.
+ * Returns 0 if there aren't any timers.
+ */
+double mg_mgr_min_timer(const struct mg_mgr *mgr);
+
 #ifdef __cplusplus
 }
 #endif /* __cplusplus */
@@ -3875,6 +3880,7 @@ struct mg_mgr {
 #endif
   void *user_data; /* User data */
   int num_ifaces;
+  int num_calls;
   struct mg_iface **ifaces; /* network interfaces */
   const char *nameserver;   /* DNS server to use */
 };
@@ -3987,17 +3993,17 @@ void mg_mgr_init_opt(struct mg_mgr *mgr, void *user_data,
  *
  * Closes and deallocates all active connections.
  */
-void mg_mgr_free(struct mg_mgr *);
+void mg_mgr_free(struct mg_mgr *mgr);
 
 /*
  * This function performs the actual IO and must be called in a loop
- * (an event loop). It returns the current timestamp.
+ * (an event loop). It returns number of user events generated (except POLLs).
  * `milli` is the maximum number of milliseconds to sleep.
  * `mg_mgr_poll()` checks all connections for IO readiness. If at least one
  * of the connections is IO-ready, `mg_mgr_poll()` triggers the respective
  * event handlers and returns.
  */
-time_t mg_mgr_poll(struct mg_mgr *, int milli);
+int mg_mgr_poll(struct mg_mgr *mgr, int milli);
 
 #if MG_ENABLE_BROADCAST
 /*
diff --git a/src/common/platforms/lwip/mg_lwip.h b/src/common/platforms/lwip/mg_lwip.h
index c0d4081e88ee686c93c6c4f7d4ac65e2ab0cf349..c15e2ee2db3a1d31eb07a54667c5ba4dc6380f50 100644
--- a/src/common/platforms/lwip/mg_lwip.h
+++ b/src/common/platforms/lwip/mg_lwip.h
@@ -66,7 +66,6 @@ typedef int sock_t;
 #if MG_NET_IF == MG_NET_IF_LWIP_LOW_LEVEL
 struct mg_mgr;
 struct mg_connection;
-uint32_t mg_lwip_get_poll_delay_ms(struct mg_mgr *mgr);
 void mg_lwip_set_keepalive_params(struct mg_connection *nc, int idle,
                                   int interval, int count);
 #endif
diff --git a/src/common/platforms/lwip/mg_lwip_ev_mgr.c b/src/common/platforms/lwip/mg_lwip_ev_mgr.c
index 39937fc919b01673747b9a68da2666f9c617f1e0..b495464e91434ee337cbb533fe8a3d4d5cd0b7cd 100644
--- a/src/common/platforms/lwip/mg_lwip_ev_mgr.c
+++ b/src/common/platforms/lwip/mg_lwip_ev_mgr.c
@@ -167,36 +167,4 @@ time_t mg_lwip_if_poll(struct mg_iface *iface, int timeout_ms) {
   return now;
 }
 
-uint32_t mg_lwip_get_poll_delay_ms(struct mg_mgr *mgr) {
-  struct mg_connection *nc;
-  double now;
-  double min_timer = 0;
-  int num_timers = 0;
-  mg_ev_mgr_lwip_process_signals(mgr);
-  for (nc = mg_next(mgr, NULL); nc != NULL; nc = mg_next(mgr, nc)) {
-    struct mg_lwip_conn_state *cs = (struct mg_lwip_conn_state *) nc->sock;
-    if (nc->ev_timer_time > 0) {
-      if (num_timers == 0 || nc->ev_timer_time < min_timer) {
-        min_timer = nc->ev_timer_time;
-      }
-      num_timers++;
-    }
-    /* We want and can send data, request a poll immediately. */
-    if (nc->sock != INVALID_SOCKET && mg_lwip_if_can_send(nc, cs)) {
-      return 0;
-    }
-  }
-  uint32_t timeout_ms = ~0;
-  now = mg_time();
-  if (num_timers > 0) {
-    /* If we have a timer that is past due, do a poll ASAP. */
-    if (min_timer < now) return 0;
-    double timer_timeout_ms = (min_timer - now) * 1000 + 1 /* rounding */;
-    if (timer_timeout_ms < timeout_ms) {
-      timeout_ms = timer_timeout_ms;
-    }
-  }
-  return timeout_ms;
-}
-
 #endif /* MG_NET_IF == MG_NET_IF_LWIP_LOW_LEVEL */
diff --git a/src/mg_net.c b/src/mg_net.c
index 5c961409148cd6f57015bf56afc611550b8e2a7a..3862fa892fbfb06b74123b70ced48aec9a0b1f06 100644
--- a/src/mg_net.c
+++ b/src/mg_net.c
@@ -99,6 +99,7 @@ MG_INTERNAL void mg_call(struct mg_connection *nc,
                   (nc->flags & _MG_CALLBACK_MODIFIABLE_FLAGS_MASK);
     }
   }
+  if (ev != MG_EV_POLL) nc->mgr->num_calls++;
   if (ev != MG_EV_POLL) {
     DBG(("%p after %s flags=0x%lx rmbl=%d smbl=%d", nc,
          ev_handler == nc->handler ? "user" : "proto", nc->flags,
@@ -276,19 +277,14 @@ void mg_mgr_free(struct mg_mgr *m) {
   MG_FREE((char *) m->nameserver);
 }
 
-time_t mg_mgr_poll(struct mg_mgr *m, int timeout_ms) {
-  int i;
-  time_t now = 0; /* oh GCC, seriously ? */
-
-  if (m->num_ifaces == 0) {
-    LOG(LL_ERROR, ("cannot poll: no interfaces"));
-    return 0;
-  }
+int mg_mgr_poll(struct mg_mgr *m, int timeout_ms) {
+  int i, num_calls_before = m->num_calls;
 
   for (i = 0; i < m->num_ifaces; i++) {
-    now = m->ifaces[i]->vtable->poll(m->ifaces[i], timeout_ms);
+    m->ifaces[i]->vtable->poll(m->ifaces[i], timeout_ms);
   }
-  return now;
+
+  return (m->num_calls - num_calls_before);
 }
 
 int mg_vprintf(struct mg_connection *nc, const char *fmt, va_list ap) {
diff --git a/src/mg_net.h b/src/mg_net.h
index 90b42b47af6dc936c4fb4e282c609e8a858edc4b..d854e770a2db828d1a378904109ee8d05e40f031 100644
--- a/src/mg_net.h
+++ b/src/mg_net.h
@@ -89,6 +89,7 @@ struct mg_mgr {
 #endif
   void *user_data; /* User data */
   int num_ifaces;
+  int num_calls;
   struct mg_iface **ifaces; /* network interfaces */
   const char *nameserver;   /* DNS server to use */
 };
@@ -201,17 +202,17 @@ void mg_mgr_init_opt(struct mg_mgr *mgr, void *user_data,
  *
  * Closes and deallocates all active connections.
  */
-void mg_mgr_free(struct mg_mgr *);
+void mg_mgr_free(struct mg_mgr *mgr);
 
 /*
  * This function performs the actual IO and must be called in a loop
- * (an event loop). It returns the current timestamp.
+ * (an event loop). It returns number of user events generated (except POLLs).
  * `milli` is the maximum number of milliseconds to sleep.
  * `mg_mgr_poll()` checks all connections for IO readiness. If at least one
  * of the connections is IO-ready, `mg_mgr_poll()` triggers the respective
  * event handlers and returns.
  */
-time_t mg_mgr_poll(struct mg_mgr *, int milli);
+int mg_mgr_poll(struct mg_mgr *mgr, int milli);
 
 #if MG_ENABLE_BROADCAST
 /*
diff --git a/src/mg_net_if.c b/src/mg_net_if.c
index 2a93dcfd4a8440d235b8cf39d911bc213cf0e4a5..c91feb9960d5040570144b75e8986a06fba77956 100644
--- a/src/mg_net_if.c
+++ b/src/mg_net_if.c
@@ -39,3 +39,15 @@ struct mg_iface *mg_find_iface(struct mg_mgr *mgr,
   }
   return NULL;
 }
+
+double mg_mgr_min_timer(const struct mg_mgr *mgr) {
+  double min_timer = 0;
+  struct mg_connection *nc;
+  for (nc = mgr->active_connections; nc != NULL; nc = nc->next) {
+    if (nc->ev_timer_time <= 0) continue;
+    if (min_timer == 0 || nc->ev_timer_time < min_timer) {
+      min_timer = nc->ev_timer_time;
+    }
+  }
+  return min_timer;
+}
diff --git a/src/mg_net_if.h b/src/mg_net_if.h
index cba58fb95fd2d4688c8e9059b913516ac7927c9e..1239d54a0ae6af2cb4092fe0b8902900c871b7e8 100644
--- a/src/mg_net_if.h
+++ b/src/mg_net_if.h
@@ -118,6 +118,12 @@ void mg_if_recv_udp_cb(struct mg_connection *nc, void *buf, int len,
 /* Deliver a POLL event to the connection. */
 int mg_if_poll(struct mg_connection *nc, double now);
 
+/*
+ * Return minimal timer value amoung connections in the manager.
+ * Returns 0 if there aren't any timers.
+ */
+double mg_mgr_min_timer(const struct mg_mgr *mgr);
+
 #ifdef __cplusplus
 }
 #endif /* __cplusplus */