Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(121)

Side by Side Diff: chrome/chrome_watcher/chrome_watcher_main.cc

Issue 848033005: Use a window to catch WM_ENDSESSION. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@sessionending_window
Patch Set: Move method definitions out of class, add some comments. Created 5 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2014 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include <windows.h> 5 #include <windows.h>
6 6
7 #include "base/at_exit.h" 7 #include "base/at_exit.h"
8 #include "base/bind.h"
8 #include "base/command_line.h" 9 #include "base/command_line.h"
9 #include "base/logging_win.h" 10 #include "base/logging_win.h"
11 #include "base/message_loop/message_loop.h"
12 #include "base/message_loop/message_loop_proxy.h"
10 #include "base/process/process.h" 13 #include "base/process/process.h"
14 #include "base/run_loop.h"
15 #include "base/sequenced_task_runner.h"
11 #include "base/synchronization/waitable_event.h" 16 #include "base/synchronization/waitable_event.h"
12 #include "base/template_util.h" 17 #include "base/template_util.h"
18 #include "base/threading/thread.h"
19 #include "base/time/time.h"
20 #include "components/browser_watcher/endsession_watcher_window_win.h"
13 #include "components/browser_watcher/exit_code_watcher_win.h" 21 #include "components/browser_watcher/exit_code_watcher_win.h"
14 #include "components/browser_watcher/exit_funnel_win.h" 22 #include "components/browser_watcher/exit_funnel_win.h"
15 #include "components/browser_watcher/watcher_main_api_win.h" 23 #include "components/browser_watcher/watcher_main_api_win.h"
16 24
17 namespace { 25 namespace {
18 26
19 // Use the same log facility as Chrome for convenience. 27 // Use the same log facility as Chrome for convenience.
20 // {7FE69228-633E-4f06-80C1-527FEA23E3A7} 28 // {7FE69228-633E-4f06-80C1-527FEA23E3A7}
21 const GUID kChromeWatcherTraceProviderName = { 29 const GUID kChromeWatcherTraceProviderName = {
22 0x7fe69228, 0x633e, 0x4f06, 30 0x7fe69228, 0x633e, 0x4f06,
23 { 0x80, 0xc1, 0x52, 0x7f, 0xea, 0x23, 0xe3, 0xa7 } }; 31 { 0x80, 0xc1, 0x52, 0x7f, 0xea, 0x23, 0xe3, 0xa7 } };
24 32
25 // The data shared from the main function to the console handler. 33 // Takes care of monitoring a browser. This class watches for a browser's exit
26 struct HandlerData { 34 // code, as well as listening for WM_ENDSESSION messages. Events are recorded
27 HandlerData() : handler_done(true /* manual_reset */, 35 // in an exit funnel, for reporting the next time Chrome runs.
28 false /* initially_signaled */) { 36 class BrowserMonitor {
37 public:
38 BrowserMonitor(base::RunLoop* run_loop, const base::char16* registry_path);
39 ~BrowserMonitor();
40
41 // Starts the monitor, returns true on success.
42 bool StartWatching(const base::char16* registry_path,
43 const base::CommandLine& cmd_line);
44 private:
erikwright (departed) 2015/01/13 21:24:04 blank line before
45 // Called from EndSessionWatcherWindow on a WM_ENDSESSION message.
46 void OnEndSession(LPARAM lparam);
47
48 // Blocking function that runs on |background_thread_|.
49 void Watch();
50
51 // Posted to main thread from Watch when browser exits.
52 void BrowserExited();
53
54 // True if BrowserExited has ran.
erikwright (departed) 2015/01/13 21:24:04 has run
55 bool browser_exited_;
56
57 // The funnel used to record events for this browser.
58 browser_watcher::ExitFunnel exit_funnel_;
59
60 browser_watcher::ExitCodeWatcher exit_code_watcher_;
61 browser_watcher::EndSessionWatcherWindow end_session_watcher_window_;
62
63 // The thread that runs Watch().
64 base::Thread background_thread_;
65
66 // The run loop for the main thread and its task runner.
67 base::RunLoop* run_loop_;
68 scoped_refptr<base::SequencedTaskRunner> main_thread_;
erikwright (departed) 2015/01/13 21:24:04 base/memory/ref_counted.h
69 };
erikwright (departed) 2015/01/13 21:24:04 disallow...
70
71 BrowserMonitor::BrowserMonitor(base::RunLoop* run_loop,
72 const base::char16* registry_path) :
73 browser_exited_(false),
74 exit_code_watcher_(registry_path),
75 end_session_watcher_window_(
76 base::Bind(&BrowserMonitor::OnEndSession, base::Unretained(this))),
erikwright (departed) 2015/01/13 21:24:04 bind_helpers.h for base::Unretained
77 background_thread_("BrowserWatcherThread"),
78 run_loop_(run_loop),
79 main_thread_(base::MessageLoopProxy::current()) {
80 }
81
82 BrowserMonitor::~BrowserMonitor() {
83 }
84
85 bool BrowserMonitor::StartWatching(const base::char16* registry_path,
86 const base::CommandLine& cmd_line) {
87 if (!exit_code_watcher_.ParseArguments(cmd_line))
88 return false;
89
90 if (!exit_funnel_.Init(registry_path,
91 exit_code_watcher_.process().Handle())) {
92 return false;
29 } 93 }
30 94
31 base::WaitableEvent handler_done; 95 if (!background_thread_.StartWithOptions(
32 base::ProcessHandle process; 96 base::Thread::Options(base::MessageLoop::TYPE_IO, 0))) {
33 const base::char16* registry_path; 97 return false;
34 };
35
36 HandlerData *g_handler_data = nullptr;
37
38 BOOL CALLBACK ConsoleCtrlHandler(DWORD ctl_type) {
39 if (g_handler_data && ctl_type == CTRL_LOGOFF_EVENT) {
40 // Record the watcher logoff event in the browser's exit funnel.
41 browser_watcher::ExitFunnel funnel;
42 funnel.Init(g_handler_data->registry_path, g_handler_data->process);
43 funnel.RecordEvent(L"WatcherLogoff");
44
45 // Release the main function.
46 g_handler_data->handler_done.Signal();
47 } 98 }
48 99
49 // Fall through to the next handler in turn. 100 if (!background_thread_.task_runner()->PostTask(FROM_HERE,
50 return FALSE; 101 base::Bind(&BrowserMonitor::Watch, base::Unretained(this)))) {
102 background_thread_.Stop();
103 return false;
104 }
105
106 return true;
107 }
108
109 void BrowserMonitor::OnEndSession(LPARAM lparam) {
110 DCHECK_EQ(main_thread_, base::MessageLoopProxy::current());
111
112 exit_funnel_.RecordEvent(L"WatcherLogoff");
113
114 // Belt-and-suspenders; make sure our message loop exits ASAP.
115 if (browser_exited_)
116 run_loop_->Quit();
117 }
118
119 void BrowserMonitor::Watch() {
120 // This needs to run on an IO thread.
121 DCHECK_NE(main_thread_, base::MessageLoopProxy::current());
122
123 exit_code_watcher_.WaitForExit();
124 exit_funnel_.RecordEvent(L"BrowserExit");
125
126 main_thread_->PostTask(FROM_HERE,
127 base::Bind(&BrowserMonitor::BrowserExited, base::Unretained(this)));
128 }
129
130 void BrowserMonitor::BrowserExited() {
131 // This runs in the main thread.
132 DCHECK_EQ(main_thread_, base::MessageLoopProxy::current());
133
134 // Note that the browser has exited.
135 browser_exited_ = true;
136
137 // Our background thread has served it's purpose.
138 background_thread_.Stop();
139
140 const int exit_code = exit_code_watcher_.exit_code();
141 if (exit_code >= 0 && exit_code <= 28) {
142 // The browser exited with a well-known exit code, quit this process
143 // immediately.
144 run_loop_->Quit();
145 } else {
146 // The browser exited abnormally, wait around for a little bit to see
147 // whether this instance will get a logoff message.
148 main_thread_->PostDelayedTask(FROM_HERE,
149 run_loop_->QuitClosure(),
150 base::TimeDelta::FromSeconds(30));
151 }
51 } 152 }
52 153
53 } // namespace 154 } // namespace
54 155
55 // The main entry point to the watcher, declared as extern "C" to avoid name 156 // The main entry point to the watcher, declared as extern "C" to avoid name
56 // mangling. 157 // mangling.
57 extern "C" int WatcherMain(const base::char16* registry_path) { 158 extern "C" int WatcherMain(const base::char16* registry_path) {
58 // The exit manager is in charge of calling the dtors of singletons. 159 // The exit manager is in charge of calling the dtors of singletons.
59 base::AtExitManager exit_manager; 160 base::AtExitManager exit_manager;
60 // Initialize the commandline singleton from the environment. 161 // Initialize the commandline singleton from the environment.
61 base::CommandLine::Init(0, nullptr); 162 base::CommandLine::Init(0, nullptr);
62 163
63 logging::LogEventProvider::Initialize(kChromeWatcherTraceProviderName); 164 logging::LogEventProvider::Initialize(kChromeWatcherTraceProviderName);
64 165
65 // Arrange to be shut down as late as possible, as we want to outlive 166 // Arrange to be shut down as late as possible, as we want to outlive
66 // chrome.exe in order to report its exit status. 167 // chrome.exe in order to report its exit status.
67 // TODO(siggi): Does this (windowless) process need to register a console
68 // handler too, in order to get notice of logoff events?
69 ::SetProcessShutdownParameters(0x100, SHUTDOWN_NORETRY); 168 ::SetProcessShutdownParameters(0x100, SHUTDOWN_NORETRY);
70 169
71 browser_watcher::ExitCodeWatcher exit_code_watcher(registry_path); 170 // Run a UI message loop on the main thread.
72 int ret = 1; 171 base::MessageLoop msg_loop(base::MessageLoop::TYPE_UI);
73 // Attempt to wait on our parent process, and record its exit status. 172 msg_loop.set_thread_name("WatcherMainThread");
74 if (exit_code_watcher.ParseArguments(
75 *base::CommandLine::ForCurrentProcess())) {
76 // Set up a console control handler, and provide it the data it needs
77 // to record into the browser's exit funnel.
78 HandlerData data;
79 data.process = exit_code_watcher.process().Handle();
80 data.registry_path = registry_path;
81 g_handler_data = &data;
82 ::SetConsoleCtrlHandler(&ConsoleCtrlHandler, TRUE /* Add */);
83 173
84 // Wait on the process. 174 base::RunLoop run_loop;
85 exit_code_watcher.WaitForExit(); 175 BrowserMonitor monitor(&run_loop, registry_path);
176 base::CommandLine* cmd_line = base::CommandLine::ForCurrentProcess();
177 if (!monitor.StartWatching(registry_path, *cmd_line))
178 return 1;
86 179
87 browser_watcher::ExitFunnel funnel; 180 run_loop.Run();
88 funnel.Init(registry_path, exit_code_watcher.process().Handle());
89 funnel.RecordEvent(L"BrowserExit");
90
91 // Chrome/content exit codes are currently in the range of 0-28.
92 // Anything outside this range is strange, so watch harder.
93 int exit_code = exit_code_watcher.exit_code();
94 if (exit_code < 0 || exit_code > 28) {
95 // Wait for a max of 30 seconds to see whether we get notified of logoff.
96 data.handler_done.TimedWait(base::TimeDelta::FromSeconds(30));
97 }
98
99 // Remove the console control handler.
100 // There is a potential race here, where the handler might be executing
101 // already as we fall through here. Hopefully SetConsoleCtrlHandler is
102 // synchronizing against handler execution, for there's no other way to
103 // close the race. Thankfully we'll just get snuffed out, as this is logoff.
104 ::SetConsoleCtrlHandler(&ConsoleCtrlHandler, FALSE /* Add */);
105 g_handler_data = nullptr;
106
107 ret = 0;
108 }
109 181
110 // Wind logging down. 182 // Wind logging down.
111 logging::LogEventProvider::Uninitialize(); 183 logging::LogEventProvider::Uninitialize();
112 184
113 return ret; 185 return 0;
114 } 186 }
115 187
116 static_assert(base::is_same<decltype(&WatcherMain), 188 static_assert(base::is_same<decltype(&WatcherMain),
117 browser_watcher::WatcherMainFunction>::value, 189 browser_watcher::WatcherMainFunction>::value,
118 "WatcherMain() has wrong type"); 190 "WatcherMain() has wrong type");
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698