blob: 07e34867195f67376e67fcbfc592bdf01b928dc6 [file] [log] [blame]
// Copyright 2021 The Chromium OS 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 MISSIVE_SCHEDULER_SCHEDULER_H_
#define MISSIVE_SCHEDULER_SCHEDULER_H_
#include <memory>
#include <vector>
#include "missive/util/shared_queue.h"
#include "missive/util/status.h"
namespace reporting {
// Scheduler manages the running jobs ensuring that we don't overload the
// system memory. It runs in three modes:
// 1. NORMAL: In normal mode Scheduler will schedule up to 5 concurrent jobs
// keeping the rest in the |jobs_queue_|.
// 2. REDUCED: In reduced mode Scheduler will schedule up to 2 concurrent jobs,
// although any currently running jobs are allowed to finish.
// 3. OFF: In this mode Scheduler will enqueue no new jobs, all currently
// running jobs are allowed to finish. Jobs in the |jobs_queue_| will be
// cancelled.
class Scheduler {
public:
// A Job is a unit of work with a common interface. |StartImpl|,
// |ReportCompletionImpl|, and |CancelImpl| need to be overriden to implement
// the specific job functionality.
// To protect work from being corrupted, most of the public functions only
// work when the job is in the NOT_RUNNING state.
class Job {
public:
// NOT_RUNNING is the inital state of a Job, no methods have been called and
// it is waiting for the Scheduler to Start it.
// RUNNING is a protected state for the job, only the job can move it to
// another state.
// COMPLETED is a successful terminal state for the job.
// CANCELLED is an unsuccessful terminal state for the job.
enum class JobState { NOT_RUNNING, RUNNING, COMPLETED, CANCELLED };
// JobDelegate is responsible for sending responses to any
// listeners.
class JobDelegate {
public:
JobDelegate() = default;
virtual ~JobDelegate() = default;
private:
// Job should be the only class calling Complete or Cancel;
friend Job;
// Comple and Cancel will be called by Job::Finish and should notify
// listeners of the Jobs completion.
virtual Status Complete() = 0;
virtual Status Cancel(Status status) = 0;
};
explicit Job(std::unique_ptr<JobDelegate> job_response_delegate);
virtual ~Job() = default;
Job(const Job&) = delete;
Job& operator=(const Job&) = delete;
// If the job is not currently NOT_RUNNING, will simply return.
void Start(base::OnceCallback<void(Status)> complete_cb);
// If the job is not currently NOT_RUNNING, will simply return.
// Cancel move the job to the CANCELLED state and call |cancel_callback_|
// with the provided Status.
// Job cannot be started after a cancellation, so care must be taken to only
// cancel when appropriate.
Status Cancel(Status status);
// Returns the |job_state_| at the time of calling.
JobState GetJobState() const;
protected:
// StartImpl should perform the unit of work for the Job and call Finish
// upon completion.
virtual void StartImpl() = 0;
// Finish will call either report_completion_callback_ or cancel_callback_
// based on the provided status. In addition it will also update job_state_
// appropriately.
void Finish(Status status);
std::unique_ptr<JobDelegate> job_response_delegate_;
private:
std::atomic<JobState> job_state_{JobState::NOT_RUNNING};
// |complete_cb_| is set by |Start| and called by |Finish|.
base::OnceCallback<void(Status)> complete_cb_;
};
// SchedulerObserver allows introspection into the goings on of the Scheudler.
class SchedulerObserver {
public:
enum class Notification {
// A job has successfully been enqueued.
ACCEPTED_JOB,
// A job was rejected from enqueuing, and cancelled.
REJECTED_JOB,
// A job attempted to acquire a JobBlocker and was unable to do so.
BLOCKED_JOB,
// A job was started.
STARTED_JOB,
// Set if a job is successfully completed.
SUCCESSFUL_COMPLETION,
// Set if a job was unsuccessful in completion.
UNSUCCESSFUL_COMPLETION,
// A job was cancelled due to memory pressure.
MEMORY_PRESSURE_CANCELLATION,
};
SchedulerObserver() = default;
~SchedulerObserver() = default;
SchedulerObserver(const SchedulerObserver& other) = delete;
SchedulerObserver& operator=(const SchedulerObserver& other) = delete;
virtual void Notify(Notification notification) = 0;
};
Scheduler();
~Scheduler();
void AddObserver(SchedulerObserver* observer);
void NotifyObservers(SchedulerObserver::Notification notification);
// EnqueueJob will store the job in the |job_queue_|, and it will be executed
// as long as system memory remains above CRITICAL.
void EnqueueJob(std::unique_ptr<Job> job);
private:
class JobContext;
class JobBlocker;
class JobSemaphore;
void OnJobEnqueued();
void StartJobs();
void OnJobPop(std::unique_ptr<JobBlocker> job_blocker,
StatusOr<std::unique_ptr<Job>> job_result);
void ClearQueue();
void OnJobQueueSwap(base::queue<std::unique_ptr<Job>> job_queue) const;
// TODO(1174889) Currently unused, once resourced implements
// MemoryPressureLevels update. Also initialize JobSemaphorePool at
// TaskLimit::OFF instead of NORMAL, so that it is off until we know the
// memory pressure level.
// void UpdateMemoryPressureLevel(
// base::MemoryPressureListener::MemoryPressureLevel
// memory_pressure_level);
scoped_refptr<base::SequencedTaskRunner> sequenced_task_runner_;
std::unique_ptr<JobSemaphore> job_semaphore_;
scoped_refptr<SharedQueue<std::unique_ptr<Job>>> jobs_queue_;
std::vector<SchedulerObserver*> observers_;
base::WeakPtrFactory<Scheduler> weak_ptr_factory_{this};
};
} // namespace reporting
#endif // MISSIVE_SCHEDULER_SCHEDULER_H_