| Index: third_party/crashpad/crashpad/util/win/session_end_watcher.cc | 
| diff --git a/third_party/crashpad/crashpad/util/win/session_end_watcher.cc b/third_party/crashpad/crashpad/util/win/session_end_watcher.cc | 
| new file mode 100644 | 
| index 0000000000000000000000000000000000000000..e795f22a582824b58e9f3b5bdcc6425a8e4e3994 | 
| --- /dev/null | 
| +++ b/third_party/crashpad/crashpad/util/win/session_end_watcher.cc | 
| @@ -0,0 +1,243 @@ | 
| +// Copyright 2017 The Crashpad Authors. All rights reserved. | 
| +// | 
| +// Licensed under the Apache License, Version 2.0 (the "License"); | 
| +// you may not use this file except in compliance with the License. | 
| +// You may obtain a copy of the License at | 
| +// | 
| +//     http://www.apache.org/licenses/LICENSE-2.0 | 
| +// | 
| +// Unless required by applicable law or agreed to in writing, software | 
| +// distributed under the License is distributed on an "AS IS" BASIS, | 
| +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
| +// See the License for the specific language governing permissions and | 
| +// limitations under the License. | 
| + | 
| +#include "util/win/session_end_watcher.h" | 
| + | 
| +#include "base/logging.h" | 
| +#include "base/scoped_generic.h" | 
| + | 
| +extern "C" { | 
| +extern IMAGE_DOS_HEADER __ImageBase; | 
| +}  // extern "C" | 
| + | 
| +namespace crashpad { | 
| + | 
| +namespace { | 
| + | 
| +class ScopedSetEvent { | 
| + public: | 
| +  explicit ScopedSetEvent(HANDLE event) : event_(event) {} | 
| +  ~ScopedSetEvent() { | 
| +    if (!SetEvent(event_)) { | 
| +      PLOG(ERROR) << "SetEvent"; | 
| +    } | 
| +  } | 
| + | 
| + private: | 
| +  HANDLE event_; | 
| + | 
| +  DISALLOW_COPY_AND_ASSIGN(ScopedSetEvent); | 
| +}; | 
| + | 
| +// ScopedWindowClass and ScopedWindow operate on ATOM* and HWND*, respectively, | 
| +// instead of ATOM and HWND, so that the actual storage can exist as a local | 
| +// variable or a member variable, and the scoper can be responsible for | 
| +// releasing things only if the actual storage hasn’t been released and zeroed | 
| +// already by something else. | 
| +struct ScopedWindowClassTraits { | 
| +  static ATOM* InvalidValue() { return nullptr; } | 
| +  static void Free(ATOM* window_class) { | 
| +    if (*window_class) { | 
| +      if (!UnregisterClass(MAKEINTATOM(*window_class), 0)) { | 
| +        PLOG(ERROR) << "UnregisterClass"; | 
| +      } else { | 
| +        *window_class = 0; | 
| +      } | 
| +    } | 
| +  } | 
| +}; | 
| +using ScopedWindowClass = base::ScopedGeneric<ATOM*, ScopedWindowClassTraits>; | 
| + | 
| +struct ScopedWindowTraits { | 
| +  static HWND* InvalidValue() { return nullptr; } | 
| +  static void Free(HWND* window) { | 
| +    if (*window) { | 
| +      if (!DestroyWindow(*window)) { | 
| +        PLOG(ERROR) << "DestroyWindow"; | 
| +      } else { | 
| +        *window = nullptr; | 
| +      } | 
| +    } | 
| +  } | 
| +}; | 
| +using ScopedWindow = base::ScopedGeneric<HWND*, ScopedWindowTraits>; | 
| + | 
| +// GetWindowLongPtr()’s return value doesn’t unambiguously indicate whether it | 
| +// was successful, because 0 could either represent successful retrieval of the | 
| +// value 0, or failure. This wrapper is more convenient to use. | 
| +bool GetWindowLongPtrAndSuccess(HWND window, int index, LONG_PTR* value) { | 
| +  SetLastError(ERROR_SUCCESS); | 
| +  *value = GetWindowLongPtr(window, index); | 
| +  return *value || GetLastError() == ERROR_SUCCESS; | 
| +} | 
| + | 
| +// SetWindowLongPtr() has the same problem as GetWindowLongPtr(). Use this | 
| +// wrapper instead. | 
| +bool SetWindowLongPtrAndGetSuccess(HWND window, int index, LONG_PTR value) { | 
| +  SetLastError(ERROR_SUCCESS); | 
| +  LONG_PTR previous = SetWindowLongPtr(window, index, value); | 
| +  return previous || GetLastError() == ERROR_SUCCESS; | 
| +} | 
| + | 
| +}  // namespace | 
| + | 
| +SessionEndWatcher::SessionEndWatcher() | 
| +    : Thread(), | 
| +      window_(nullptr), | 
| +      started_(nullptr), | 
| +      stopped_(nullptr) { | 
| +  // Set bManualReset for these events so that WaitForStart() and WaitForStop() | 
| +  // can be called multiple times. | 
| + | 
| +  started_.reset(CreateEvent(nullptr, true, false, nullptr)); | 
| +  PLOG_IF(ERROR, !started_.get()) << "CreateEvent"; | 
| + | 
| +  stopped_.reset(CreateEvent(nullptr, true, false, nullptr)); | 
| +  PLOG_IF(ERROR, !stopped_.get()) << "CreateEvent"; | 
| + | 
| +  Start(); | 
| +} | 
| + | 
| +SessionEndWatcher::~SessionEndWatcher() { | 
| +  // Tear everything down by posting a WM_CLOSE to the window. This obviously | 
| +  // can’t work until the window has been created, and that happens on a | 
| +  // different thread, so wait for the start event to be signaled first. | 
| +  WaitForStart(); | 
| +  if (window_) { | 
| +    if (!PostMessage(window_, WM_CLOSE, 0, 0)) { | 
| +      PLOG(ERROR) << "PostMessage"; | 
| +    } | 
| +  } | 
| + | 
| +  Join(); | 
| +  DCHECK(!window_); | 
| +} | 
| + | 
| +void SessionEndWatcher::WaitForStart() { | 
| +  if (WaitForSingleObject(started_.get(), INFINITE) != WAIT_OBJECT_0) { | 
| +    PLOG(ERROR) << "WaitForSingleObject"; | 
| +  } | 
| +} | 
| + | 
| +void SessionEndWatcher::WaitForStop() { | 
| +  if (WaitForSingleObject(stopped_.get(), INFINITE) != WAIT_OBJECT_0) { | 
| +    PLOG(ERROR) << "WaitForSingleObject"; | 
| +  } | 
| +} | 
| + | 
| +void SessionEndWatcher::ThreadMain() { | 
| +  ATOM atom = 0; | 
| +  ScopedWindowClass window_class(&atom); | 
| +  ScopedWindow window(&window_); | 
| + | 
| +  ScopedSetEvent call_set_stop(stopped_.get()); | 
| + | 
| +  { | 
| +    ScopedSetEvent call_set_start(started_.get()); | 
| + | 
| +    WNDCLASS wndclass = {}; | 
| +    wndclass.lpfnWndProc = WindowProc; | 
| +    wndclass.hInstance = reinterpret_cast<HMODULE>(&__ImageBase); | 
| +    wndclass.lpszClassName = L"crashpad_SessionEndWatcher"; | 
| +    atom = RegisterClass(&wndclass); | 
| +    if (!atom) { | 
| +      PLOG(ERROR) << "RegisterClass"; | 
| +      return; | 
| +    } | 
| + | 
| +    window_ = CreateWindow(MAKEINTATOM(atom),  // lpClassName | 
| +                           nullptr,  // lpWindowName | 
| +                           0,  // dwStyle | 
| +                           0,  // x | 
| +                           0,  // y | 
| +                           0,  // nWidth | 
| +                           0,  // nHeight | 
| +                           nullptr,  // hWndParent | 
| +                           nullptr,  // hMenu | 
| +                           nullptr,  // hInstance | 
| +                           this);  // lpParam | 
| +    if (!window_) { | 
| +      PLOG(ERROR) << "CreateWindow"; | 
| +      return; | 
| +    } | 
| +  } | 
| + | 
| +  MSG message; | 
| +  BOOL rv = 0; | 
| +  while (window_ && (rv = GetMessage(&message, window_, 0, 0)) > 0) { | 
| +    TranslateMessage(&message); | 
| +    DispatchMessage(&message); | 
| +  } | 
| +  if (window_ && rv == -1) { | 
| +    PLOG(ERROR) << "GetMessage"; | 
| +    return; | 
| +  } | 
| +} | 
| + | 
| +// static | 
| +LRESULT CALLBACK SessionEndWatcher::WindowProc(HWND window, | 
| +                                               UINT message, | 
| +                                               WPARAM w_param, | 
| +                                               LPARAM l_param) { | 
| +  // Figure out which object this is. A pointer to it is stuffed into the last | 
| +  // parameter of CreateWindow(), which shows up as CREATESTRUCT::lpCreateParams | 
| +  // in a WM_CREATE message. That should be processed before any of the other | 
| +  // messages of interest to this function. Once the object is known, save a | 
| +  // pointer to it in the GWLP_USERDATA slot for later retrieval when processing | 
| +  // other messages. | 
| +  SessionEndWatcher* self; | 
| +  if (!GetWindowLongPtrAndSuccess( | 
| +          window, GWLP_USERDATA, reinterpret_cast<LONG_PTR*>(&self))) { | 
| +    PLOG(ERROR) << "GetWindowLongPtr"; | 
| +  } | 
| +  if (!self && message == WM_CREATE) { | 
| +    CREATESTRUCT* create = reinterpret_cast<CREATESTRUCT*>(l_param); | 
| +    self = reinterpret_cast<SessionEndWatcher*>(create->lpCreateParams); | 
| +    if (!SetWindowLongPtrAndGetSuccess( | 
| +            window, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(self))) { | 
| +      PLOG(ERROR) << "SetWindowLongPtr"; | 
| +    } | 
| +  } | 
| + | 
| +  if (self) { | 
| +    if (message == WM_ENDSESSION) { | 
| +      // If w_param is false, this WM_ENDSESSION message cancels a previous | 
| +      // WM_QUERYENDSESSION. | 
| +      if (w_param) { | 
| +        self->SessionEnding(); | 
| + | 
| +        // If the session is ending, post a close message which will kick off | 
| +        // window destruction and cause the message loop thread to terminate. | 
| +        if (!PostMessage(self->window_, WM_CLOSE, 0, 0)) { | 
| +          PLOG(ERROR) << "PostMessage"; | 
| +        } | 
| +      } | 
| +    } else if (message == WM_DESTROY) { | 
| +      // The window is being destroyed. Clear GWLP_USERDATA so that |self| won’t | 
| +      // be found during a subsequent call into this function for this window. | 
| +      // Clear self->window_ too, because it refers to an object that soon won’t | 
| +      // exist. That signals the message loop to stop processing messages. | 
| +      if (!SetWindowLongPtrAndGetSuccess(window, GWLP_USERDATA, 0)) { | 
| +        PLOG(ERROR) << "SetWindowLongPtr"; | 
| +      } | 
| +      self->window_ = nullptr; | 
| +    } | 
| +  } | 
| + | 
| +  // If the message is WM_CLOSE, DefWindowProc() will call DestroyWindow(), and | 
| +  // this function will be called again with a WM_DESTROY message. | 
| +  return DefWindowProc(window, message, w_param, l_param); | 
| +} | 
| + | 
| +}  // namespace crashpad | 
|  |