// 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.
#ifndef CHROME_BROWSER_ASH_SCHEDQOS_DBUS_SCHEDQOS_STATE_HANDLER_H_
#define CHROME_BROWSER_ASH_SCHEDQOS_DBUS_SCHEDQOS_STATE_HANDLER_H_
#include <map>
#include "base/memory/ref_counted.h"
#include "base/memory/weak_ptr.h"
#include "base/process/process.h"
#include "base/process/process_priority_delegate.h"
#include "base/sequence_checker.h"
#include "base/synchronization/lock.h"
#include "base/task/sequenced_task_runner.h"
#include "base/threading/cross_process_platform_thread_delegate.h"
#include "base/threading/platform_thread.h"
#include "base/threading/thread_type_delegate.h"
#include "base/timer/elapsed_timer.h"
#include "chrome/browser/ash/system/procfs_util.h"
#include "dbus/dbus_result.h"
namespace ash {
// DBusSchedQOSStateHandler sends `SetProcessState` and `SetThreadState` DBus
// requests to resourced on `base::Process::SetPriority()` and
// `base::PlatformThread::SetThreadType()` instead of updating OS scheduler
// settings directly.
//
// DBusSchedQOSStateHandler is for ChromeOS only.
//
// DBusSchedQOSStateHandler caches process priorities to be consistent with
// `base::Process::GetPriority()`. Processes which will change their priorities
// need to call `base::Process::InitializePriority()` before calling
// `base::Process::SetPriority()`. Otherwise `base::Process::SetPriority()`
// fails. Also the processes must call `base::Process::ForgetPriority()` when
// they terminate. Otherwise the cache in this class leaks.
class DBusSchedQOSStateHandler
: public base::ProcessPriorityDelegate,
public base::ThreadTypeDelegate,
public base::CrossProcessPlatformThreadDelegate {
public:
DBusSchedQOSStateHandler(const DBusSchedQOSStateHandler&) = delete;
DBusSchedQOSStateHandler& operator=(const DBusSchedQOSStateHandler&) = delete;
~DBusSchedQOSStateHandler() override;
// These values are persisted to logs. Entries should not be renumbered and
// numeric values should never be reused.
enum class PidReuseResult {
kNotPidReuseOnFail = 0,
kPidReuseOnFail = 1,
kNotPidReuseOnSuccess = 2,
kPidReuseOnSuccess = 3,
kMaxValue = kPidReuseOnSuccess,
};
// Creates a SandboxedProcessThreadTypeHandler instance and stores it to
// g_instance. Make sure the g_instance doesn't exist before creation.
//
// `main_task_runner` is the main thread's task runner to call D-Bus clients
// from.
static DBusSchedQOSStateHandler* Create(
scoped_refptr<base::SequencedTaskRunner> main_task_runner);
// base::ProcessPriorityDelegate :
bool CanSetProcessPriority() override;
void InitializeProcessPriority(base::ProcessId process_id) override;
void ForgetProcessPriority(base::ProcessId process_id) override;
bool SetProcessPriority(base::ProcessId process_id,
base::Process::Priority priority) override;
base::Process::Priority GetProcessPriority(
base::ProcessId process_id) override;
bool HandleThreadTypeChange(base::ProcessId process_id,
base::PlatformThreadId thread_id,
base::ThreadType thread_type) override;
bool HandleThreadTypeChange(base::PlatformThreadId thread_id,
base::ThreadType thread_type) override;
private:
struct ProcessState {
base::Process::Priority priority;
bool need_retry;
std::map<base::PlatformThreadId, base::ThreadType>
preconnected_thread_types;
explicit ProcessState(base::Process::Priority priority);
ProcessState() = delete;
ProcessState(base::Process::Priority priority, bool need_retry);
~ProcessState();
ProcessState(ProcessState&&);
ProcessState(ProcessState&) = delete;
};
explicit DBusSchedQOSStateHandler(
scoped_refptr<base::SequencedTaskRunner> main_task_runner);
void CheckResourcedDisconnected(dbus::DBusResult result);
void OnServiceConnected(bool success);
void SetProcessPriorityOnThread(base::ProcessId process_id,
base::Process::Priority priority);
void OnSetProcessPriorityFinish(base::ProcessId process_id,
base::Process::Priority priority,
base::ElapsedTimer elapsed_timer,
system::ProcStatFile stat_file,
dbus::DBusResult result);
void MarkProcessToRetry(base::ProcessId process_id);
void SetThreadTypeOnThread(base::ProcessId process_id,
base::PlatformThreadId thread_id,
base::ThreadType thread_type);
void OnSetThreadTypeFinish(base::ProcessId process_id,
base::PlatformThreadId thread_id,
base::ThreadType thread_type,
base::ElapsedTimer elapsed_timer,
system::ProcStatFile stat_file,
dbus::DBusResult result);
void AddThreadRetryEntry(base::ProcessId process_id,
base::PlatformThreadId thread_id,
base::ThreadType thread_type);
SEQUENCE_CHECKER(sequence_checker_);
bool is_connected_ GUARDED_BY_CONTEXT(sequence_checker_) = false;
base::Lock process_state_map_lock_;
std::map<base::ProcessId, ProcessState> process_state_map_
GUARDED_BY(process_state_map_lock_);
// ResourcedClient need to be called on the main thread.
scoped_refptr<base::SequencedTaskRunner> main_task_runner_;
base::WeakPtrFactory<DBusSchedQOSStateHandler> weak_ptr_factory_{this};
};
} // namespace ash
#endif // CHROME_BROWSER_ASH_SCHEDQOS_DBUS_SCHEDQOS_STATE_HANDLER_H_