blob: 38d2dea07c472a4b564c47d9647440a27dc9e6a7 [file] [log] [blame]
// Copyright 2024 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/windows_services/elevated_tracing_service/process_watcher.h"
#include <windows.h>
#include <array>
#include <utility>
#include "base/check.h"
#include "base/check_op.h"
#include "base/functional/bind.h"
#include "base/functional/callback.h"
#include "base/location.h"
#include "base/memory/scoped_refptr.h"
#include "base/task/task_runner.h"
#include "base/task/task_traits.h"
#include "base/task/thread_pool.h"
#include "base/threading/scoped_blocking_call.h"
#include "base/win/scoped_handle.h"
namespace elevated_tracing_service {
namespace {
// Waits for either the watched process to terminate or for the shutdown event
// to be signaled. In the former case, the `on_terminated` closure is
// run before exiting.
void WatchInThreadPool(base::Process process,
base::OnceClosure on_terminated,
HANDLE startup_event,
base::win::ScopedHandle shutdown_event) {
// Signal that the task is ready to watch.
::SetEvent(std::exchange(startup_event, nullptr));
DWORD result;
{
base::ScopedBlockingCall will_block(FROM_HERE,
base::BlockingType::WILL_BLOCK);
HANDLE handles[] = {process.Handle(), shutdown_event.get()};
result = ::WaitForMultipleObjects(std::size(handles), &handles[0],
/*bWaitAll=*/FALSE,
/*dwMilliseconds=*/INFINITE);
}
CHECK_NE(result, WAIT_FAILED);
if (result == WAIT_OBJECT_0) {
std::move(on_terminated).Run();
} // else the shutdown event was signaled.
}
} // namespace
ProcessWatcher::ProcessWatcher(base::Process process,
base::OnceClosure on_terminated) {
// An event that is signaled by the watch task when it is ready to watch. No
// need to duplicate this for the watch task, as this instance will outlive
// the `SetEvent()` call in the task.
base::WaitableEvent startup_event;
// Prepare a dup of the shutdown event for the task to wait on.
HANDLE shutdown_event = nullptr;
CHECK(::DuplicateHandle(::GetCurrentProcess(), shutdown_event_.handle(),
::GetCurrentProcess(), &shutdown_event,
/*dwDesiredAccess=*/0,
/*bInheritHandle=*/FALSE, DUPLICATE_SAME_ACCESS));
base::ThreadPool::CreateTaskRunner(
{base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN, base::MayBlock()})
->PostTask(
FROM_HERE,
base::BindOnce(&WatchInThreadPool, std::move(process),
std::move(on_terminated), startup_event.handle(),
base::win::ScopedHandle(shutdown_event)));
// Wait for the watch task to signal that it is ready.
startup_event.Wait();
}
ProcessWatcher::~ProcessWatcher() {
// Signal that the watch task should exit if it is still watching the process.
shutdown_event_.Signal();
}
} // namespace elevated_tracing_service