blob: 5b0105e5e9ccdde0eff7bef1e4d6bf29bbd4cd0e [file] [log] [blame]
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_CHROME_CLEANER_ENGINES_TARGET_SANDBOX_REQUEST_HELPER_H_
#define CHROME_CHROME_CLEANER_ENGINES_TARGET_SANDBOX_REQUEST_HELPER_H_
#include <memory>
#include <utility>
#include "base/bind.h"
#include "base/callback_forward.h"
#include "base/memory/scoped_refptr.h"
#include "base/message_loop/message_loop.h"
#include "base/message_loop/message_loop_current.h"
#include "base/run_loop.h"
#include "base/task_runner.h"
#include "base/task_runner_util.h"
#include "base/threading/thread_task_runner_handle.h"
#include "chrome/chrome_cleaner/engines/common/sandbox_error_code.h"
namespace chrome_cleaner {
// A basic struct to return the state of the Mojo request, including any
// specific error messages.
struct MojoCallStatus {
enum State {
MOJO_CALL_MADE,
MOJO_CALL_ERROR,
} state;
SandboxErrorCode error_code;
static MojoCallStatus Success();
static MojoCallStatus Failure(SandboxErrorCode error_code);
};
// These functions are included in the header so SyncSandboxRequest can access
// them, they shouldn't be used by anyone else.
namespace internal {
void SaveMojoCallStatus(base::OnceClosure quit_closure,
MojoCallStatus* status_out,
MojoCallStatus status);
// Returns a wrapper that executes |closure| on the given |task_runner|.
base::OnceClosure ClosureForTaskRunner(
scoped_refptr<base::TaskRunner> task_runner,
base::OnceClosure closure);
} // namespace internal
// This template function is inlined in the header because otherwise we would
// have to introduce a templated class, and then the callsites would no longer
// be able to implicit call the right function, but would have to explicitly
// create the class.
template <typename ProxyType,
typename RequestCallback,
typename ResponseCallback>
MojoCallStatus SyncSandboxRequest(ProxyType* proxy,
RequestCallback request,
ResponseCallback response) {
if (!proxy) {
return MojoCallStatus::Failure(SandboxErrorCode::INTERNAL_ERROR);
}
// If there is not already a MessageLoop on this thread (eg. when called on a
// thread created by a 3rd-party engine), create one.
std::unique_ptr<base::MessageLoop> local_loop;
if (!base::MessageLoopCurrent::IsSet())
local_loop = std::make_unique<base::MessageLoop>();
// Start a local RunLoop to receive the results from the request. It will
// execute until either the response callback or the early response callback
// causes it to quit, as follows:
//
// 1. If an error occurs that prevents the asynchronous Mojo call from being
// made, the early response callback will quit the loop. The response
// callback will not be called.
//
// 2. Otherwise as soon as the asynchronous Mojo call is made, the early
// response callback will be invoked with MOJO_CALL_MADE, and the loop will
// continue.
//
// 3. When the asynchronous Mojo call returns a response, the response
// callback will be called and will quit the loop.
base::RunLoop run_loop(base::RunLoop::Type::kNestableTasksAllowed);
MojoCallStatus call_status;
auto early_response_callback = base::BindOnce(
&internal::SaveMojoCallStatus, run_loop.QuitClosure(), &call_status);
// The response callback will be executed on the Mojo IO thread, so it will
// need to hop back to |message_loop|'s task runner to quit the run loop.
auto response_callback = base::BindOnce(
std::move(response),
internal::ClosureForTaskRunner(base::ThreadTaskRunnerHandle::Get(),
run_loop.QuitClosure()));
// Invoke the asynchronous Mojo call on the Mojo IO thread.
base::PostTaskAndReplyWithResult(
proxy->task_runner().get(), FROM_HERE,
base::BindOnce(std::move(request), std::move(response_callback)),
std::move(early_response_callback));
run_loop.Run();
return call_status;
}
template <typename ProxyType,
typename RequestCallback,
typename ResponseCallback>
MojoCallStatus SyncSandboxRequest(scoped_refptr<ProxyType> proxy,
RequestCallback request,
ResponseCallback response) {
return SyncSandboxRequest(proxy.get(), std::move(request),
std::move(response));
}
} // namespace chrome_cleaner
#endif // CHROME_CHROME_CLEANER_ENGINES_TARGET_SANDBOX_REQUEST_HELPER_H_