unix: update loop->time after poll
Fixes a bug where timers expire prematurely when the following conditions hold: a) libuv first spends some time blocked in the platform poll function b) a callback then calls uv_timer_start() Cause: uv_timer_start() uses an out-of-date loop->time in its 'when should the timer callback run?' calculations. Solution: Update loop->time before invoking any callbacks. Fixes #678.
This commit is contained in:
parent
339033afc0
commit
2f55353490
@ -286,7 +286,7 @@ int uv_run2(uv_loop_t* loop, uv_run_mode mode) {
|
||||
return 0;
|
||||
|
||||
do {
|
||||
uv_update_time(loop);
|
||||
uv__update_time(loop);
|
||||
uv__run_timers(loop);
|
||||
uv__run_idle(loop);
|
||||
uv__run_prepare(loop);
|
||||
@ -307,7 +307,7 @@ int uv_run(uv_loop_t* loop) {
|
||||
|
||||
|
||||
void uv_update_time(uv_loop_t* loop) {
|
||||
loop->time = uv__hrtime() / 1000000;
|
||||
uv__update_time(loop);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -105,14 +105,6 @@ enum {
|
||||
UV_TCP_SINGLE_ACCEPT = 0x400 /* Only accept() when idle. */
|
||||
};
|
||||
|
||||
__attribute__((unused))
|
||||
static void uv__req_init(uv_loop_t* loop, uv_req_t* req, uv_req_type type) {
|
||||
req->type = type;
|
||||
uv__req_register(loop, req);
|
||||
}
|
||||
#define uv__req_init(loop, req, type) \
|
||||
uv__req_init((loop), (uv_req_t*)(req), (type))
|
||||
|
||||
/* core */
|
||||
void uv__handle_init(uv_loop_t* loop, uv_handle_t* handle, uv_handle_type type);
|
||||
int uv__nonblock(int fd, int set);
|
||||
@ -238,4 +230,17 @@ static const int kFSEventStreamEventFlagItemIsSymlink = 0x00040000;
|
||||
|
||||
#endif /* defined(__APPLE__) */
|
||||
|
||||
__attribute__((unused))
|
||||
static void uv__req_init(uv_loop_t* loop, uv_req_t* req, uv_req_type type) {
|
||||
req->type = type;
|
||||
uv__req_register(loop, req);
|
||||
}
|
||||
#define uv__req_init(loop, req, type) \
|
||||
uv__req_init((loop), (uv_req_t*)(req), (type))
|
||||
|
||||
__attribute__((unused))
|
||||
static void uv__update_time(uv_loop_t* loop) {
|
||||
loop->time = uv__hrtime() / 1000000;
|
||||
}
|
||||
|
||||
#endif /* UV_UNIX_INTERNAL_H_ */
|
||||
|
||||
@ -141,6 +141,12 @@ void uv__io_poll(uv_loop_t* loop, int timeout) {
|
||||
ARRAY_SIZE(events),
|
||||
timeout == -1 ? NULL : &spec);
|
||||
|
||||
/* Update loop->time unconditionally. It's tempting to skip the update when
|
||||
* timeout == 0 (i.e. non-blocking poll) but there is no guarantee that the
|
||||
* operating system didn't reschedule our process while in the syscall.
|
||||
*/
|
||||
SAVE_ERRNO(uv__update_time(loop));
|
||||
|
||||
if (nfds == 0) {
|
||||
assert(timeout != -1);
|
||||
return;
|
||||
@ -244,10 +250,7 @@ void uv__io_poll(uv_loop_t* loop, int timeout) {
|
||||
update_timeout:
|
||||
assert(timeout > 0);
|
||||
|
||||
diff = uv__hrtime() / 1000000;
|
||||
assert(diff >= base);
|
||||
diff -= base;
|
||||
|
||||
diff = loop->time - base;
|
||||
if (diff >= (uint64_t) timeout)
|
||||
return;
|
||||
|
||||
|
||||
@ -182,6 +182,12 @@ void uv__io_poll(uv_loop_t* loop, int timeout) {
|
||||
ARRAY_SIZE(events),
|
||||
timeout);
|
||||
|
||||
/* Update loop->time unconditionally. It's tempting to skip the update when
|
||||
* timeout == 0 (i.e. non-blocking poll) but there is no guarantee that the
|
||||
* operating system didn't reschedule our process while in the syscall.
|
||||
*/
|
||||
SAVE_ERRNO(uv__update_time(loop));
|
||||
|
||||
if (nfds == 0) {
|
||||
assert(timeout != -1);
|
||||
return;
|
||||
@ -243,10 +249,7 @@ void uv__io_poll(uv_loop_t* loop, int timeout) {
|
||||
update_timeout:
|
||||
assert(timeout > 0);
|
||||
|
||||
diff = uv__hrtime() / 1000000;
|
||||
assert(diff >= base);
|
||||
diff -= base;
|
||||
|
||||
diff = loop->time - base;
|
||||
if (diff >= (uint64_t) timeout)
|
||||
return;
|
||||
|
||||
|
||||
@ -150,6 +150,12 @@ void uv__io_poll(uv_loop_t* loop, int timeout) {
|
||||
abort();
|
||||
}
|
||||
|
||||
/* Update loop->time unconditionally. It's tempting to skip the update when
|
||||
* timeout == 0 (i.e. non-blocking poll) but there is no guarantee that the
|
||||
* operating system didn't reschedule our process while in the syscall.
|
||||
*/
|
||||
SAVE_ERRNO(uv__update_time(loop));
|
||||
|
||||
if (events[0].portev_source == 0) {
|
||||
if (timeout == 0)
|
||||
return;
|
||||
@ -211,10 +217,7 @@ void uv__io_poll(uv_loop_t* loop, int timeout) {
|
||||
update_timeout:
|
||||
assert(timeout > 0);
|
||||
|
||||
diff = uv__hrtime() / 1000000;
|
||||
assert(diff >= base);
|
||||
diff -= base;
|
||||
|
||||
diff = loop->time - base;
|
||||
if (diff >= (uint64_t) timeout)
|
||||
return;
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user