blob: 59e728178e1ae40d597db981fc3374682d7fb72e [file] [log] [blame]
// Copyright 2016 The Android Open Source Project
//
// This software is licensed under the terms of the GNU General Public
// License version 2, as published by the Free Software Foundation, and
// may be copied, distributed, and modified under those terms.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
#pragma once
#include "android/base/async/Looper.h"
#include "android/base/async/RecurrentTask.h"
#include "android/base/Compiler.h"
#include "android/base/Optional.h"
#include "android/base/synchronization/Lock.h"
#include "android/base/system/System.h"
#include "android/metrics/MetricsReporter.h"
#include <functional>
#include <list>
#include <map>
#include <memory>
namespace android {
namespace metrics {
// PeriodicReporter - a class that helps one to implement metrics reporting
// task that needs to run recurrently, with a specific timeout between the runs.
//
// This class optimizes the number of messages sent by combining all tasks added
// with the same reporting peroid into a single metrics message.
//
// To create such task, call the addTask() method, supplying the run period and
// the task callback.
// Task callback is invoked as a regular MetricsReporter callback; it may return
// |false| if there's no need to log anything at the current invocation.
//
// If your task needs to be able to stop reporting metrics at some point, use
// addCancelableTask() and destroy the returned token when the task isn't needed
// anymore.
//
// To actually start running the tasks, call start() method passing the metrics
// reporter instance and a looper to use for the recurring tasks. Before that,
// PeriodicReporter would be in a 'collect-only' mode where it collects all
// tasks added in addTask()/addCancelableTask(), but never runs those.
// stop() call stops and deletes all recrurring tasks and switches the
// PeriodicReporter back to 'collect-only' mode.
//
class PeriodicReporter final
: public std::enable_shared_from_this<PeriodicReporter> {
DISALLOW_COPY_ASSIGN_AND_MOVE(PeriodicReporter);
using WeakPtr = std::weak_ptr<PeriodicReporter>;
using InternalCallback = MetricsReporter::ConditionalCallback;
// The container choice - list<> - here is to make sure the iterators are
// preserved on all inserts or erases of other elements. We store the
// iterator to the inserted element to be able to remove it later.
using CallbackList = std::list<InternalCallback>;
// A private guard class that removes the task when destroyed.
class TaskGuard final {
DISALLOW_COPY_ASSIGN_AND_MOVE(TaskGuard);
public:
using RemoverFunc = std::function<void()>;
TaskGuard(RemoverFunc func) : taskRemover(std::move(func)) {}
~TaskGuard() { taskRemover(); }
private:
RemoverFunc taskRemover;
};
public:
// Forward-declare the singleton instance holder: this allows it to call
// the private constructor.
struct InstanceHolder;
using TaskToken = std::shared_ptr<TaskGuard>;
using Callback = InternalCallback;
using Ptr = std::shared_ptr<PeriodicReporter>;
// Start PeriodicReporter using the supplied |metricsReporter| for reporting
// and |looper| to run periodic tasks.
// You can add tasks even before this call; they just won't run until the
// reporter is started.
static void start(MetricsReporter* metricsReporter, base::Looper* looper);
// Stop the currently running PeriodicReporter instance. This finalizes and
// deletes all currently running tasks and reverts back to a mode where the
// reporter collects all tasks but never runs them.
static void stop();
// Return the currently active instance.
static PeriodicReporter& get();
~PeriodicReporter();
// Add a new periodic reporting task that runs every |periodMs| milliseconds
void addTask(base::System::Duration periodMs, Callback callback);
// Same as addTask(), but the task can be canceled by destroying the
// returned TaskToken.
TaskToken addCancelableTask(base::System::Duration periodMs,
Callback callback);
private:
// A set of callbacks added with the same period, and a recurrent task that
// invokes them.
struct PerPeriodData {
base::Optional<base::RecurrentTask> task;
CallbackList callbacks;
};
// Private constructor to make sure we only create a shared_ptr<>-owneed
// instances (needed for the enable_shared_from_this<> base class).
PeriodicReporter();
// Update the internal MetricsReporter and Looper and start processing.
void startImpl(MetricsReporter* metricsReporter, base::Looper* looper);
void createPerPeriodTimerNoLock(PerPeriodData* dataPtr,
base::System::Duration periodMs);
// The implementation of task adding.
// |mLock| has to be help during the call.
CallbackList::iterator addTaskInternalNoLock(
base::System::Duration periodMs,
Callback callback);
void removeTask(base::System::Duration periodMs,
CallbackList::iterator iter);
MetricsReporter* mMetricsReporter = nullptr;
base::Looper* mLooper = nullptr;
base::Lock mLock;
// A collection of tasks grouped by their run period.
// Note: std::map<> is required here as it doesn't invalidate its iterators
// on all operations other than erasing the specific element for that
// iterator. We store the iterators (and, actually, addresses of stored
// PerPeriodData objects) and later use them, so they *must* be stable.
std::map<base::System::Duration, PerPeriodData> mPeriodDataByPeriod;
};
} // namespace metrics
} // namespace android