Skip to content

Commit

Permalink
win: evaluate timers when system wakes up
Browse files Browse the repository at this point in the history
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: nodejs/node#6763
PR-URL: libuv#962
Reviewed-By: Ben Noordhuis <[email protected]>
Reviewed-By: Saúl Ibarra Corretgé <[email protected]>
  • Loading branch information
saghul committed Aug 18, 2016
1 parent 83341fb commit 4cbf513
Show file tree
Hide file tree
Showing 7 changed files with 206 additions and 3 deletions.
1 change: 1 addition & 0 deletions Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ AM_CPPFLAGS += -I$(top_srcdir)/src/win \
-D_WIN32_WINNT=0x0600
libuv_la_SOURCES += src/win/async.c \
src/win/core.c \
src/win/detect-wakeup.c \
src/win/dl.c \
src/win/error.c \
src/win/fs-event.c \
Expand Down
115 changes: 112 additions & 3 deletions src/win/core.c
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@

#include "uv.h"
#include "internal.h"
#include "queue.h"
#include "handle-inl.h"
#include "req-inl.h"

Expand Down Expand Up @@ -78,6 +79,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. */
Expand All @@ -99,6 +192,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.
*/
Expand All @@ -118,6 +214,9 @@ static void uv_init(void) {

/* Initialize utilities */
uv__util_init();

/* Initialize system wakeup detection */
uv__init_detect_system_wakeup();
}


Expand Down Expand Up @@ -173,6 +272,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:
Expand All @@ -194,6 +297,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;
Expand Down Expand Up @@ -267,9 +372,13 @@ static void uv__loop_poll(uv_loop_t* loop, DWORD timeout) {

if (success) {
for (i = 0; i < count; i++) {
/* Package was dequeued */
req = container_of(overlappeds[i].lpOverlapped, uv_req_t, u.io.overlapped);
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 = container_of(overlappeds[i].lpOverlapped, uv_req_t, u.io.overlapped);
uv_insert_pending_req(loop, req);
}
}

/* Some time might have passed waiting for I/O,
Expand Down
35 changes: 35 additions & 0 deletions src/win/detect-wakeup.c
Original file line number Diff line number Diff line change
@@ -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,
&registration_handle);
}
10 changes: 10 additions & 0 deletions src/win/internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -379,4 +379,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_ */
10 changes: 10 additions & 0 deletions src/win/winapi.c
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,14 @@ sNtQuerySystemInformation pNtQuerySystemInformation;
/* Kernel32 function pointers */


/* 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) {
Expand Down Expand Up @@ -99,4 +104,9 @@ void uv_winapi_init() {
uv_fatal_error(GetLastError(), "GetModuleHandleA");
}

powrprof_module = LoadLibraryA("powrprof.dll");
if (powrprof_module != NULL) {
pPowerRegisterSuspendResumeNotification = (sPowerRegisterSuspendResumeNotification)
GetProcAddress(powrprof_module, "PowerRegisterSuspendResumeNotification");
}
}
37 changes: 37 additions & 0 deletions src/win/winapi.h
Original file line number Diff line number Diff line change
Expand Up @@ -4620,6 +4620,39 @@ typedef NTSTATUS (NTAPI *sNtQueryDirectoryFile)
# define ERROR_MUI_FILE_NOT_LOADED 15105
#endif

/* 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;
Expand All @@ -4633,4 +4666,8 @@ extern sNtQuerySystemInformation pNtQuerySystemInformation;
/* Kernel32 function pointers */



/* Powrprof.dll function pointer */
extern sPowerRegisterSuspendResumeNotification pPowerRegisterSuspendResumeNotification;

#endif /* UV_WIN_WINAPI_H_ */
1 change: 1 addition & 0 deletions uv.gyp
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@
'include/uv-win.h',
'src/win/async.c',
'src/win/core.c',
'src/win/detect-wakeup.c',
'src/win/dl.c',
'src/win/error.c',
'src/win/fs.c',
Expand Down

0 comments on commit 4cbf513

Please sign in to comment.