win: evaluate timers when system wakes up
When Windows resumes after sleep GetQueuedCompletionStatus timeout is not updated. This commit adds a method for signaling all loops to wake up and update their timers. Fixes: https://github.com/nodejs/node/issues/6763 PR-URL: https://github.com/libuv/libuv/pull/962 Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl> Reviewed-By: Saúl Ibarra Corretgé <saghul@gmail.com>
This commit is contained in:
parent
791c817891
commit
6fa3524ea7
@ -48,6 +48,7 @@ AM_CPPFLAGS += -I$(top_srcdir)/src/win \
|
||||
libuv_la_SOURCES += src/win/async.c \
|
||||
src/win/atomicops-inl.h \
|
||||
src/win/core.c \
|
||||
src/win/detect-wakeup.c \
|
||||
src/win/dl.c \
|
||||
src/win/error.c \
|
||||
src/win/fs-event.c \
|
||||
|
||||
@ -48,6 +48,7 @@ OBJS = src/fs-poll.o \
|
||||
src/version.o \
|
||||
src/win/async.o \
|
||||
src/win/core.o \
|
||||
src/win/detect-wakeup.o \
|
||||
src/win/dl.o \
|
||||
src/win/error.o \
|
||||
src/win/fs-event.o \
|
||||
|
||||
115
src/win/core.c
115
src/win/core.c
@ -31,6 +31,7 @@
|
||||
|
||||
#include "uv.h"
|
||||
#include "internal.h"
|
||||
#include "queue.h"
|
||||
#include "handle-inl.h"
|
||||
#include "req-inl.h"
|
||||
|
||||
@ -80,6 +81,98 @@ static void uv__crt_invalid_parameter_handler(const wchar_t* expression,
|
||||
}
|
||||
#endif
|
||||
|
||||
static uv_loop_t** uv__loops;
|
||||
static int uv__loops_size;
|
||||
static int uv__loops_capacity;
|
||||
#define UV__LOOPS_CHUNK_SIZE 8
|
||||
static uv_mutex_t uv__loops_lock;
|
||||
|
||||
static void uv__loops_init() {
|
||||
uv_mutex_init(&uv__loops_lock);
|
||||
uv__loops = uv__calloc(UV__LOOPS_CHUNK_SIZE, sizeof(uv_loop_t*));
|
||||
if (!uv__loops)
|
||||
uv_fatal_error(ERROR_OUTOFMEMORY, "uv__malloc");
|
||||
uv__loops_size = 0;
|
||||
uv__loops_capacity = UV__LOOPS_CHUNK_SIZE;
|
||||
}
|
||||
|
||||
static int uv__loops_add(uv_loop_t* loop) {
|
||||
uv_loop_t** new_loops;
|
||||
int new_capacity, i;
|
||||
|
||||
uv_mutex_lock(&uv__loops_lock);
|
||||
|
||||
if (uv__loops_size == uv__loops_capacity) {
|
||||
new_capacity = uv__loops_capacity + UV__LOOPS_CHUNK_SIZE;
|
||||
new_loops = uv__realloc(uv__loops, sizeof(uv_loop_t*) * new_capacity);
|
||||
if (!new_loops)
|
||||
goto failed_loops_realloc;
|
||||
uv__loops = new_loops;
|
||||
for (i = uv__loops_capacity; i < new_capacity; ++i)
|
||||
uv__loops[i] = NULL;
|
||||
uv__loops_capacity = new_capacity;
|
||||
}
|
||||
uv__loops[uv__loops_size] = loop;
|
||||
++uv__loops_size;
|
||||
|
||||
uv_mutex_unlock(&uv__loops_lock);
|
||||
return 0;
|
||||
|
||||
failed_loops_realloc:
|
||||
uv_mutex_unlock(&uv__loops_lock);
|
||||
return ERROR_OUTOFMEMORY;
|
||||
}
|
||||
|
||||
static void uv__loops_remove(uv_loop_t* loop) {
|
||||
int loop_index;
|
||||
int smaller_capacity;
|
||||
uv_loop_t** new_loops;
|
||||
|
||||
uv_mutex_lock(&uv__loops_lock);
|
||||
|
||||
for (loop_index = 0; loop_index < uv__loops_size; ++loop_index) {
|
||||
if (uv__loops[loop_index] == loop)
|
||||
break;
|
||||
}
|
||||
/* If loop was not found, ignore */
|
||||
if (loop_index == uv__loops_size)
|
||||
goto loop_removed;
|
||||
|
||||
uv__loops[loop_index] = uv__loops[uv__loops_size - 1];
|
||||
uv__loops[uv__loops_size - 1] = NULL;
|
||||
--uv__loops_size;
|
||||
|
||||
/* If we didn't grow to big skip downsizing */
|
||||
if (uv__loops_capacity < 4 * UV__LOOPS_CHUNK_SIZE)
|
||||
goto loop_removed;
|
||||
|
||||
/* Downsize only if more than half of buffer is free */
|
||||
smaller_capacity = uv__loops_capacity / 2;
|
||||
if (uv__loops_size >= smaller_capacity)
|
||||
goto loop_removed;
|
||||
new_loops = uv__realloc(uv__loops, sizeof(uv_loop_t*) * smaller_capacity);
|
||||
if (!new_loops)
|
||||
goto loop_removed;
|
||||
uv__loops = new_loops;
|
||||
uv__loops_capacity = smaller_capacity;
|
||||
|
||||
loop_removed:
|
||||
uv_mutex_unlock(&uv__loops_lock);
|
||||
}
|
||||
|
||||
void uv__wake_all_loops() {
|
||||
int i;
|
||||
uv_loop_t* loop;
|
||||
|
||||
uv_mutex_lock(&uv__loops_lock);
|
||||
for (i = 0; i < uv__loops_size; ++i) {
|
||||
loop = uv__loops[i];
|
||||
assert(loop);
|
||||
if (loop->iocp != INVALID_HANDLE_VALUE)
|
||||
PostQueuedCompletionStatus(loop->iocp, 0, 0, NULL);
|
||||
}
|
||||
uv_mutex_unlock(&uv__loops_lock);
|
||||
}
|
||||
|
||||
static void uv_init(void) {
|
||||
/* Tell Windows that we will handle critical errors. */
|
||||
@ -101,6 +194,9 @@ static void uv_init(void) {
|
||||
_CrtSetReportHook(uv__crt_dbg_report_handler);
|
||||
#endif
|
||||
|
||||
/* Initialize tracking of all uv loops */
|
||||
uv__loops_init();
|
||||
|
||||
/* Fetch winapi function pointers. This must be done first because other
|
||||
* initialization code might need these function pointers to be loaded.
|
||||
*/
|
||||
@ -120,6 +216,9 @@ static void uv_init(void) {
|
||||
|
||||
/* Initialize utilities */
|
||||
uv__util_init();
|
||||
|
||||
/* Initialize system wakeup detection */
|
||||
uv__init_detect_system_wakeup();
|
||||
}
|
||||
|
||||
|
||||
@ -178,6 +277,10 @@ int uv_loop_init(uv_loop_t* loop) {
|
||||
uv__handle_unref(&loop->wq_async);
|
||||
loop->wq_async.flags |= UV__HANDLE_INTERNAL;
|
||||
|
||||
err = uv__loops_add(loop);
|
||||
if (err)
|
||||
goto fail_async_init;
|
||||
|
||||
return 0;
|
||||
|
||||
fail_async_init:
|
||||
@ -199,6 +302,8 @@ void uv__once_init(void) {
|
||||
void uv__loop_close(uv_loop_t* loop) {
|
||||
size_t i;
|
||||
|
||||
uv__loops_remove(loop);
|
||||
|
||||
/* close the async handle without needing an extra loop iteration */
|
||||
assert(!loop->wq_async.async_sent);
|
||||
loop->wq_async.close_cb = NULL;
|
||||
@ -323,9 +428,13 @@ static void uv_poll_ex(uv_loop_t* loop, DWORD timeout) {
|
||||
|
||||
if (success) {
|
||||
for (i = 0; i < count; i++) {
|
||||
/* Package was dequeued */
|
||||
req = uv_overlapped_to_req(overlappeds[i].lpOverlapped);
|
||||
uv_insert_pending_req(loop, req);
|
||||
/* Package was dequeued, but see if it is not a empty package
|
||||
* meant only to wake us up.
|
||||
*/
|
||||
if (overlappeds[i].lpOverlapped) {
|
||||
req = uv_overlapped_to_req(overlappeds[i].lpOverlapped);
|
||||
uv_insert_pending_req(loop, req);
|
||||
}
|
||||
}
|
||||
|
||||
/* Some time might have passed waiting for I/O,
|
||||
|
||||
35
src/win/detect-wakeup.c
Normal file
35
src/win/detect-wakeup.c
Normal file
@ -0,0 +1,35 @@
|
||||
#include "uv.h"
|
||||
#include "internal.h"
|
||||
#include "winapi.h"
|
||||
|
||||
static void uv__register_system_resume_callback();
|
||||
|
||||
void uv__init_detect_system_wakeup() {
|
||||
/* Try registering system power event callback. This is the cleanest
|
||||
* method, but it will only work on Win8 and above.
|
||||
*/
|
||||
uv__register_system_resume_callback();
|
||||
}
|
||||
|
||||
static ULONG CALLBACK uv__system_resume_callback(PVOID Context,
|
||||
ULONG Type,
|
||||
PVOID Setting) {
|
||||
if (Type == PBT_APMRESUMESUSPEND || Type == PBT_APMRESUMEAUTOMATIC)
|
||||
uv__wake_all_loops();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void uv__register_system_resume_callback() {
|
||||
_DEVICE_NOTIFY_SUBSCRIBE_PARAMETERS recipient;
|
||||
_HPOWERNOTIFY registration_handle;
|
||||
|
||||
if (pPowerRegisterSuspendResumeNotification == NULL)
|
||||
return;
|
||||
|
||||
recipient.Callback = uv__system_resume_callback;
|
||||
recipient.Context = NULL;
|
||||
(*pPowerRegisterSuspendResumeNotification)(DEVICE_NOTIFY_CALLBACK,
|
||||
&recipient,
|
||||
®istration_handle);
|
||||
}
|
||||
@ -381,4 +381,14 @@ extern int uv_tcp_non_ifs_lsp_ipv6;
|
||||
extern struct sockaddr_in uv_addr_ip4_any_;
|
||||
extern struct sockaddr_in6 uv_addr_ip6_any_;
|
||||
|
||||
/*
|
||||
* Wake all loops with fake message
|
||||
*/
|
||||
void uv__wake_all_loops();
|
||||
|
||||
/*
|
||||
* Init system wake-up detection
|
||||
*/
|
||||
void uv__init_detect_system_wakeup();
|
||||
|
||||
#endif /* UV_WIN_INTERNAL_H_ */
|
||||
|
||||
@ -49,9 +49,14 @@ sCancelSynchronousIo pCancelSynchronousIo;
|
||||
sGetFinalPathNameByHandleW pGetFinalPathNameByHandleW;
|
||||
|
||||
|
||||
/* Powrprof.dll function pointer */
|
||||
sPowerRegisterSuspendResumeNotification pPowerRegisterSuspendResumeNotification;
|
||||
|
||||
|
||||
void uv_winapi_init() {
|
||||
HMODULE ntdll_module;
|
||||
HMODULE kernel32_module;
|
||||
HMODULE powrprof_module;
|
||||
|
||||
ntdll_module = GetModuleHandleA("ntdll.dll");
|
||||
if (ntdll_module == NULL) {
|
||||
@ -143,4 +148,12 @@ void uv_winapi_init() {
|
||||
|
||||
pGetFinalPathNameByHandleW = (sGetFinalPathNameByHandleW)
|
||||
GetProcAddress(kernel32_module, "GetFinalPathNameByHandleW");
|
||||
|
||||
|
||||
powrprof_module = LoadLibraryA("powrprof.dll");
|
||||
if (powrprof_module != NULL) {
|
||||
pPowerRegisterSuspendResumeNotification = (sPowerRegisterSuspendResumeNotification)
|
||||
GetProcAddress(powrprof_module, "PowerRegisterSuspendResumeNotification");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -4684,6 +4684,40 @@ typedef DWORD (WINAPI* sGetFinalPathNameByHandleW)
|
||||
DWORD cchFilePath,
|
||||
DWORD dwFlags);
|
||||
|
||||
/* from powerbase.h */
|
||||
#ifndef DEVICE_NOTIFY_CALLBACK
|
||||
# define DEVICE_NOTIFY_CALLBACK 2
|
||||
#endif
|
||||
|
||||
#ifndef PBT_APMRESUMEAUTOMATIC
|
||||
# define PBT_APMRESUMEAUTOMATIC 18
|
||||
#endif
|
||||
|
||||
#ifndef PBT_APMRESUMESUSPEND
|
||||
# define PBT_APMRESUMESUSPEND 7
|
||||
#endif
|
||||
|
||||
typedef ULONG CALLBACK _DEVICE_NOTIFY_CALLBACK_ROUTINE(
|
||||
PVOID Context,
|
||||
ULONG Type,
|
||||
PVOID Setting
|
||||
);
|
||||
typedef _DEVICE_NOTIFY_CALLBACK_ROUTINE* _PDEVICE_NOTIFY_CALLBACK_ROUTINE;
|
||||
|
||||
typedef struct _DEVICE_NOTIFY_SUBSCRIBE_PARAMETERS {
|
||||
_PDEVICE_NOTIFY_CALLBACK_ROUTINE Callback;
|
||||
PVOID Context;
|
||||
} _DEVICE_NOTIFY_SUBSCRIBE_PARAMETERS, *_PDEVICE_NOTIFY_SUBSCRIBE_PARAMETERS;
|
||||
|
||||
typedef PVOID _HPOWERNOTIFY;
|
||||
typedef _HPOWERNOTIFY *_PHPOWERNOTIFY;
|
||||
|
||||
typedef DWORD (WINAPI *sPowerRegisterSuspendResumeNotification)
|
||||
(DWORD Flags,
|
||||
HANDLE Recipient,
|
||||
_PHPOWERNOTIFY RegistrationHandle);
|
||||
|
||||
|
||||
/* Ntdll function pointers */
|
||||
extern sRtlNtStatusToDosError pRtlNtStatusToDosError;
|
||||
extern sNtDeviceIoControlFile pNtDeviceIoControlFile;
|
||||
@ -4707,4 +4741,8 @@ extern sWakeConditionVariable pWakeConditionVariable;
|
||||
extern sCancelSynchronousIo pCancelSynchronousIo;
|
||||
extern sGetFinalPathNameByHandleW pGetFinalPathNameByHandleW;
|
||||
|
||||
|
||||
/* Powrprof.dll function pointer */
|
||||
extern sPowerRegisterSuspendResumeNotification pPowerRegisterSuspendResumeNotification;
|
||||
|
||||
#endif /* UV_WIN_WINAPI_H_ */
|
||||
|
||||
Loading…
Reference in New Issue
Block a user