1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86
|
// 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
|