[PATCH v2 2/3] ntdll: Implement SystemExecutionState handling.

Chip Davis cdavis at codeweavers.com
Wed Aug 14 14:06:22 CDT 2019


Get it from the server in NtPowerInformation(), and change it when
NtSetThreadExecutionState() is called.

Signed-off-by: Chip Davis <cdavis at codeweavers.com>
---

Notes:
    v2: Handle thread termination.

 dlls/ntdll/nt.c                | 29 ++++++-----
 dlls/powrprof/tests/powrprof.c | 16 +++---
 include/winnt.h                |  1 +
 server/Makefile.in             |  1 +
 server/power.c                 | 90 ++++++++++++++++++++++++++++++++++
 server/protocol.def            | 13 +++++
 server/thread.c                |  2 +
 server/thread.h                |  2 +
 8 files changed, 135 insertions(+), 19 deletions(-)
 create mode 100644 server/power.c

diff --git a/dlls/ntdll/nt.c b/dlls/ntdll/nt.c
index a9985fbc224..7af01a775c1 100644
--- a/dlls/ntdll/nt.c
+++ b/dlls/ntdll/nt.c
@@ -3062,15 +3062,18 @@ NTSTATUS WINAPI NtInitiatePowerAction(
  */
 NTSTATUS WINAPI NtSetThreadExecutionState( EXECUTION_STATE new_state, EXECUTION_STATE *old_state )
 {
-    static EXECUTION_STATE current =
-        ES_SYSTEM_REQUIRED | ES_DISPLAY_REQUIRED | ES_USER_PRESENT;
-    *old_state = current;
+    NTSTATUS status;
 
     WARN( "(0x%x, %p): stub, harmless.\n", new_state, old_state );
 
-    if (!(current & ES_CONTINUOUS) || (new_state & ES_CONTINUOUS))
-        current = new_state;
-    return STATUS_SUCCESS;
+    SERVER_START_REQ( set_thread_execution_state )
+    {
+        req->new_state = new_state;
+        status = wine_server_call( req );
+        *old_state = reply->old_state;
+    }
+    SERVER_END_REQ;
+    return status;
 }
 
 /******************************************************************************
@@ -3189,13 +3192,17 @@ NTSTATUS WINAPI NtPowerInformation(
 			return STATUS_SUCCESS;
 		}
 		case SystemExecutionState: {
-			PULONG ExecutionState = lpOutputBuffer;
-			WARN("semi-stub: SystemExecutionState\n"); /* Needed for .NET Framework, but using a FIXME is really noisy. */
+			EXECUTION_STATE *exec_state = lpOutputBuffer;
+			NTSTATUS status;
 			if (lpInputBuffer != NULL)
 				return STATUS_INVALID_PARAMETER;
-			/* FIXME: The actual state should be the value set by SetThreadExecutionState which is not currently implemented. */
-			*ExecutionState = ES_USER_PRESENT;
-			return STATUS_SUCCESS;
+			SERVER_START_REQ( get_system_execution_state )
+			{
+				status = wine_server_call( req );
+				if (!status) *exec_state = reply->exec_state;
+			}
+			SERVER_END_REQ;
+			return status;
 		}
 		case ProcessorInformation: {
 			const int cannedMHz = 1000; /* We fake a 1GHz processor if we can't conjure up real values */
diff --git a/dlls/powrprof/tests/powrprof.c b/dlls/powrprof/tests/powrprof.c
index d744895cc48..ce9f96bfd90 100644
--- a/dlls/powrprof/tests/powrprof.c
+++ b/dlls/powrprof/tests/powrprof.c
@@ -42,7 +42,7 @@ static void test_system_execution_state(void)
     ok(status == STATUS_SUCCESS, "status %08x\n", status);
 
     old_es = SetThreadExecutionState(ES_SYSTEM_REQUIRED);
-    todo_wine ok(old_es == ES_CONTINUOUS, "unexpected execution state 0x%08x\n", old_es);
+    ok(old_es == ES_CONTINUOUS, "unexpected execution state 0x%08x\n", old_es);
 
     old_es = es;
     status = CallNtPowerInformation(SystemExecutionState, NULL, 0, &es, sizeof(es));
@@ -50,11 +50,11 @@ static void test_system_execution_state(void)
     ok(es == old_es, "unexpected execution state 0x%08x vs 0x%08x\n", es, old_es);
 
     old_es = SetThreadExecutionState(ES_CONTINUOUS | ES_DISPLAY_REQUIRED);
-    todo_wine ok(old_es == ES_CONTINUOUS, "unexpected execution state 0x%08x\n", old_es);
+    ok(old_es == ES_CONTINUOUS, "unexpected execution state 0x%08x\n", old_es);
 
     status = CallNtPowerInformation(SystemExecutionState, NULL, 0, &es, sizeof(es));
     ok(status == STATUS_SUCCESS, "status %08x\n", status);
-    todo_wine ok(es & ES_DISPLAY_REQUIRED, "unexpected execution state 0x%08x\n", es);
+    ok(es & ES_DISPLAY_REQUIRED, "unexpected execution state 0x%08x\n", es);
 
     old_es = SetThreadExecutionState(ES_CONTINUOUS);
     ok(old_es == (ES_CONTINUOUS|ES_DISPLAY_REQUIRED), "unexpected execution state 0x%08x\n", old_es);
@@ -127,23 +127,23 @@ static void test_system_execution_state_other_thread(void)
 
     status = CallNtPowerInformation(SystemExecutionState, NULL, 0, &es, sizeof(es));
     ok(status == STATUS_SUCCESS, "status %08x\n", status);
-    todo_wine ok(es & ES_SYSTEM_REQUIRED, "unexpected execution state 0x%08x\n", es);
+    ok(es & ES_SYSTEM_REQUIRED, "unexpected execution state 0x%08x\n", es);
     es = SetThreadExecutionState(0);
-    todo_wine ok(es == ES_CONTINUOUS, "unexpected execution state 0x%08x\n", es);
+    ok(es == ES_CONTINUOUS, "unexpected execution state 0x%08x\n", es);
     SignalObjectAndWait(events[1], events[0], INFINITE, FALSE);
 
     status = CallNtPowerInformation(SystemExecutionState, NULL, 0, &es, sizeof(es));
     ok(status == STATUS_SUCCESS, "status %08x\n", status);
-    todo_wine ok(es & ES_DISPLAY_REQUIRED, "unexpected execution state 0x%08x\n", es);
+    ok(es & ES_DISPLAY_REQUIRED, "unexpected execution state 0x%08x\n", es);
     es = SetThreadExecutionState(0);
-    todo_wine ok(es == ES_CONTINUOUS, "unexpected execution state 0x%08x\n", es);
+    ok(es == ES_CONTINUOUS, "unexpected execution state 0x%08x\n", es);
     SignalObjectAndWait(events[1], thread, INFINITE, FALSE);
 
     status = CallNtPowerInformation(SystemExecutionState, NULL, 0, &es, sizeof(es));
     ok(status == STATUS_SUCCESS, "status %08x\n", status);
     ok(es == base_es, "unexpected execution state 0x%08x\n", es);
     es = SetThreadExecutionState(0);
-    todo_wine ok(es == ES_CONTINUOUS, "unexpected execution state 0x%08x\n", es);
+    ok(es == ES_CONTINUOUS, "unexpected execution state 0x%08x\n", es);
 
     CloseHandle(thread);
     CloseHandle(events[0]);
diff --git a/include/winnt.h b/include/winnt.h
index 87b4adbfea0..e94ca65f0aa 100644
--- a/include/winnt.h
+++ b/include/winnt.h
@@ -906,6 +906,7 @@ typedef enum _HEAP_INFORMATION_CLASS {
 #define ES_SYSTEM_REQUIRED    0x00000001
 #define ES_DISPLAY_REQUIRED   0x00000002
 #define ES_USER_PRESENT       0x00000004
+#define ES_AWAYMODE_REQUIRED  0x00000040
 #define ES_CONTINUOUS         0x80000000
 
 /* The Win32 register context */
diff --git a/server/Makefile.in b/server/Makefile.in
index b39bd30305b..e619d8223fb 100644
--- a/server/Makefile.in
+++ b/server/Makefile.in
@@ -27,6 +27,7 @@ C_SRCS = \
 	procfs.c \
 	ptrace.c \
 	queue.c \
+	power.c \
 	region.c \
 	registry.c \
 	request.c \
diff --git a/server/power.c b/server/power.c
new file mode 100644
index 00000000000..d629dac0218
--- /dev/null
+++ b/server/power.c
@@ -0,0 +1,90 @@
+/*
+ * Power management support
+ *
+ * Copyright (C) 1998 Alexandre Julliard
+ * Copyright (C) 2003 Mike McCormack
+ * Copyright (C) 2005 Robert Shearman
+ * Copyright (C) 2019 Chip Davis for CodeWeavers
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#include "config.h"
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdarg.h>
+
+#include "ntstatus.h"
+#define WIN32_NO_STATUS
+#include "windef.h"
+#include "winternl.h"
+
+#include "handle.h"
+#include "request.h"
+#include "thread.h"
+#include "unicode.h"
+
+static unsigned int sys_count = 0;  /* number of holds on system sleep */
+static unsigned int disp_count = 0; /* number of holds on display sleep */
+static unsigned int away_count = 0; /* number of away mode requests */
+
+void release_thread_execution_state( struct thread *thread )
+{
+    if (thread->exec_state & ES_SYSTEM_REQUIRED)
+        --sys_count;
+    if (thread->exec_state & ES_DISPLAY_REQUIRED)
+        --disp_count;
+    if (thread->exec_state & ES_AWAYMODE_REQUIRED)
+        --away_count;
+}
+
+
+/* Get the current system execution state */
+DECL_HANDLER(get_system_execution_state)
+{
+    reply->exec_state = 0;
+    if (sys_count != 0)
+        reply->exec_state |= ES_SYSTEM_REQUIRED;
+    if (disp_count != 0)
+        reply->exec_state |= ES_DISPLAY_REQUIRED;
+    if (away_count != 0)
+        reply->exec_state |= ES_AWAYMODE_REQUIRED;
+}
+
+/* Set the current thread's execution state */
+DECL_HANDLER(set_thread_execution_state)
+{
+    reply->old_state = current->exec_state;
+
+    if (!(req->new_state & ES_CONTINUOUS))
+        return;
+
+    if ((current->exec_state & ES_SYSTEM_REQUIRED) && !(req->new_state & ES_SYSTEM_REQUIRED))
+        --sys_count;
+    else if (!(current->exec_state & ES_SYSTEM_REQUIRED) && (req->new_state & ES_SYSTEM_REQUIRED))
+        ++sys_count;
+
+    if ((current->exec_state & ES_DISPLAY_REQUIRED) && !(req->new_state & ES_DISPLAY_REQUIRED))
+        --disp_count;
+    else if (!(current->exec_state & ES_DISPLAY_REQUIRED) && (req->new_state & ES_DISPLAY_REQUIRED))
+        ++disp_count;
+
+    if ((current->exec_state & ES_AWAYMODE_REQUIRED) && !(req->new_state & ES_AWAYMODE_REQUIRED))
+        --away_count;
+    else if (!(current->exec_state & ES_AWAYMODE_REQUIRED) && (req->new_state & ES_AWAYMODE_REQUIRED))
+        ++away_count;
+    current->exec_state = req->new_state;
+}
diff --git a/server/protocol.def b/server/protocol.def
index 8157199f2fa..22640bb625b 100644
--- a/server/protocol.def
+++ b/server/protocol.def
@@ -3944,3 +3944,16 @@ struct handle_info
 @REQ(resume_process)
     obj_handle_t handle;       /* process handle */
 @END
+
+/* Get the current system execution state */
+ at REQ(get_system_execution_state)
+ at REPLY
+    unsigned int exec_state;    /* current execution state */
+ at END
+
+/* Set the current thread's execution state */
+ at REQ(set_thread_execution_state)
+    unsigned int new_state;     /* thread's new execution state */
+ at REPLY
+    unsigned int old_state;     /* thread's old execution state */
+ at END
diff --git a/server/thread.c b/server/thread.c
index dc327603daa..d34d23aec6a 100644
--- a/server/thread.c
+++ b/server/thread.c
@@ -205,6 +205,7 @@ static inline void init_thread_structure( struct thread *thread )
     thread->reply_fd        = NULL;
     thread->wait_fd         = NULL;
     thread->state           = RUNNING;
+    thread->exec_state      = ES_CONTINUOUS;
     thread->exit_code       = 0;
     thread->priority        = 0;
     thread->suspend         = 0;
@@ -1223,6 +1224,7 @@ void kill_thread( struct thread *thread, int violent_death )
     wake_up( &thread->obj, 0 );
     if (violent_death) send_thread_signal( thread, SIGQUIT );
     cleanup_thread( thread );
+    release_thread_execution_state( thread );
     remove_process_thread( thread->process, thread );
     release_object( thread );
 }
diff --git a/server/thread.h b/server/thread.h
index 9f7914803de..8e29f9eda33 100644
--- a/server/thread.h
+++ b/server/thread.h
@@ -72,6 +72,7 @@ struct thread
     struct fd             *reply_fd;      /* fd to send a reply to a client */
     struct fd             *wait_fd;       /* fd to use to wake a sleeping client */
     enum run_state         state;         /* running state */
+    unsigned int           exec_state;    /* power execution state */
     int                    exit_code;     /* thread exit code */
     int                    unix_pid;      /* Unix pid of client */
     int                    unix_tid;      /* Unix tid of client */
@@ -130,6 +131,7 @@ extern int is_cpu_supported( enum cpu_type cpu );
 extern unsigned int get_supported_cpu_mask(void);
 extern int suspend_thread( struct thread *thread );
 extern int resume_thread( struct thread *thread );
+extern void release_thread_execution_state( struct thread *thread );
 
 /* ptrace functions */
 
-- 
2.21.0




More information about the wine-devel mailing list