chromium/chrome/installer/setup/setup_singleton.cc

// Copyright 2016 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/installer/setup/setup_singleton.h"

#include <functional>
#include <string>
#include <utility>

#include "base/check_op.h"
#include "base/files/file_path.h"
#include "base/memory/ptr_util.h"
#include "base/strings/string_number_conversions.h"
#include "base/time/time.h"
#include "chrome/installer/setup/installer_state.h"
#include "chrome/installer/util/installation_state.h"

namespace installer {

std::unique_ptr<SetupSingleton> SetupSingleton::Acquire(
    const base::CommandLine& command_line,
    const InitialPreferences& initial_preferences,
    InstallationState* original_state,
    InstallerState* installer_state) {
  DCHECK(original_state);
  DCHECK(installer_state);

  const std::wstring sync_primitive_name_suffix(
      base::NumberToWString(std::hash<base::FilePath::StringType>()(
          installer_state->target_path().value())));

  base::win::ScopedHandle setup_mutex(::CreateMutex(
      nullptr, FALSE,
      (L"Global\\ChromeSetupMutex_" + sync_primitive_name_suffix).c_str()));
  if (!setup_mutex.IsValid()) {
    // UMA data indicates that this happens 0.03 % of the time.
    return nullptr;
  }

  base::win::ScopedHandle exit_event(::CreateEvent(
      nullptr, TRUE, FALSE,
      (L"Global\\ChromeSetupExitEvent_" + sync_primitive_name_suffix).c_str()));
  if (!exit_event.IsValid()) {
    // UMA data indicates that this happens < 0.01 % of the time.
    return nullptr;
  }

  auto setup_singleton = base::WrapUnique(
      new SetupSingleton(std::move(setup_mutex), std::move(exit_event)));

  {
    // Acquire a mutex to ensure that a single call to SetupSingleton::Acquire()
    // signals |exit_event_| and waits for |setup_mutex_| to be released at a
    // time.
    base::win::ScopedHandle exit_event_mutex(::CreateMutex(
        nullptr, FALSE,
        (L"Global\\ChromeSetupExitEventMutex_" + sync_primitive_name_suffix)
            .c_str()));
    if (!exit_event_mutex.IsValid()) {
      // UMA data indicates that this happens < 0.01 % of the time.
      return nullptr;
    }

    ScopedHoldMutex scoped_hold_exit_event_mutex;
    if (!scoped_hold_exit_event_mutex.Acquire(exit_event_mutex.Get())) {
      // UMA data indicates that this happens < 0.01 % of the time.
      return nullptr;
    }

    // Signal |exit_event_|. This causes any call to WaitForInterrupt() on a
    // SetupSingleton bound to the same Chrome installation to return
    // immediately.
    setup_singleton->exit_event_.Signal();

    // Acquire |setup_mutex_|.
    if (!setup_singleton->scoped_hold_setup_mutex_.Acquire(
            setup_singleton->setup_mutex_.Get())) {
      // UMA data indicates that this happens 0.84 % of the time.
      return nullptr;
    }
    setup_singleton->exit_event_.Reset();
  }

  // Update |original_state| and |installer_state|.
  original_state->Initialize();
  installer_state->Initialize(command_line, initial_preferences,
                              *original_state);

  // UMA data indicates that this method succeeds > 99% of the time.
  return setup_singleton;
}

SetupSingleton::~SetupSingleton() = default;

bool SetupSingleton::WaitForInterrupt(const base::TimeDelta& max_time) const {
  const bool exit_event_signaled = exit_event_.TimedWait(max_time);
  return exit_event_signaled;
}

SetupSingleton::ScopedHoldMutex::ScopedHoldMutex() = default;

SetupSingleton::ScopedHoldMutex::~ScopedHoldMutex() {
  if (mutex_ != INVALID_HANDLE_VALUE)
    ::ReleaseMutex(mutex_);
}

bool SetupSingleton::ScopedHoldMutex::Acquire(HANDLE mutex) {
  DCHECK_NE(INVALID_HANDLE_VALUE, mutex);
  DCHECK_EQ(INVALID_HANDLE_VALUE, mutex_);

  const DWORD wait_return_value = ::WaitForSingleObject(
      mutex, static_cast<DWORD>(base::Seconds(5).InMilliseconds()));
  if (wait_return_value == WAIT_ABANDONED ||
      wait_return_value == WAIT_OBJECT_0) {
    mutex_ = mutex;
    return true;
  }

  DPCHECK(wait_return_value != WAIT_FAILED);
  return false;
}

SetupSingleton::SetupSingleton(base::win::ScopedHandle setup_mutex,
                               base::win::ScopedHandle exit_event)
    : setup_mutex_(std::move(setup_mutex)), exit_event_(std::move(exit_event)) {
  DCHECK(setup_mutex_.IsValid());
}

}  // namespace installer