/*
 * Copyright (C) 2021 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef INCLUDE_PERFETTO_EXT_BASE_PERIODIC_TASK_H_
#define INCLUDE_PERFETTO_EXT_BASE_PERIODIC_TASK_H_

#include <functional>

#include "perfetto/ext/base/scoped_file.h"
#include "perfetto/ext/base/thread_checker.h"
#include "perfetto/ext/base/weak_ptr.h"

namespace perfetto {
namespace base {

class TaskRunner;

// A periodic task utility class. It wraps the logic necessary to do periodic
// tasks using a TaskRunner, taking care of subtleties like ensuring that
// outstanding tasks are cancelled after reset/dtor.
// Tasks are aligned on wall time, this is to ensure that when using multiple
// periodic tasks, they happen at the same time, minimizing wakeups.
// On Linux/Android it also supports suspend-aware mode (via timerfd). On other
// operating systems it falls back to PostDelayedTask, which is not
// suspend-aware.
// TODO(primiano): this should probably become a periodic timer scheduler, so we
// can use one FD for everything rather than one FD per task. For now we take
// the hit of a FD-per-task to keep this low-risk.
class PeriodicTask {
 public:
  explicit PeriodicTask(base::TaskRunner*);
  ~PeriodicTask();  // Calls Reset().

  struct Args {
    uint32_t period_ms = 0;
    std::function<void()> task = nullptr;
    bool start_first_task_immediately = false;
    bool use_suspend_aware_timer = false;
  };

  void Start(Args);

  // Safe to be called multiple times, even without calling Start():
  void Reset();

  // No copy or move. WeakPtr-wrapped pointers to |this| are posted on the
  // task runner, this class is not easily movable.
  PeriodicTask(const PeriodicTask&) = delete;
  PeriodicTask& operator=(const PeriodicTask&) = delete;
  PeriodicTask(PeriodicTask&&) = delete;
  PeriodicTask& operator=(PeriodicTask&&) = delete;

  base::PlatformHandle timer_fd_for_testing() { return *timer_fd_; }

 private:
  static void RunTaskAndPostNext(base::WeakPtr<PeriodicTask>,
                                 uint32_t generation);
  void PostNextTask();
  void ResetTimerFd();

  base::TaskRunner* const task_runner_;
  Args args_;
  uint32_t generation_ = 0;
  base::ScopedPlatformHandle timer_fd_;

  PERFETTO_THREAD_CHECKER(thread_checker_)
  base::WeakPtrFactory<PeriodicTask> weak_ptr_factory_;  // Keep last.
};

}  // namespace base
}  // namespace perfetto

#endif  // INCLUDE_PERFETTO_EXT_BASE_PERIODIC_TASK_H_
