blob: 9cf964f59117d9b72835d68cbaf53ecaf5b1973a [file] [log] [blame]
// Copyright 2018 The Fuchsia 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 PERIDOT_LIB_UTIL_IDLE_WAITER_H_
#define PERIDOT_LIB_UTIL_IDLE_WAITER_H_
#include <vector>
#include <lib/async-loop/cpp/loop.h>
#include <lib/fit/function.h>
#include <src/lib/fxl/memory/ref_ptr.h>
#include <src/lib/fxl/memory/weak_ptr.h>
namespace util {
// A utility class that exposes the |WaitUntilIdle| function, which invokes a
// callback after all outstanding tasks (not including delayed tasks) have been
// processed on a task queue and all user-instrumented asynchronous activities
// have completed. This function is typically exposed on a service's debug FIDL
// interface and used for test synchronization.
//
// This class is bound to a message loop using SetLoop(). Unbound
// instances of this class may exist in unit tests.
class IdleWaiter final {
public:
class Activity : public fxl::RefCountedThreadSafe<Activity> {
public:
Activity(fxl::WeakPtr<IdleWaiter> tracker);
~Activity();
private:
fxl::WeakPtr<IdleWaiter> tracker_;
};
using ActivityToken = fxl::RefPtr<Activity>;
IdleWaiter();
~IdleWaiter();
void SetLoop(async::Loop* loop);
async::Loop* loop() const { return loop_; }
// Registers an ongoing activity which prevents this app from being
// considered idle. When the last copy of |ActivityToken| is destroyed, the
// activity is considered complete.
//
// |ActivityToken| should typically be captured in any lambda triggered while
// handling a call. It is not necessary to register an activity that completes
// synchronously.
//
// This method must be invoked on the thread that constructed |IdleWaiter|,
// and |ActivityToken| must be released on the same thread.
ActivityToken RegisterOngoingActivity();
// Checking for inactivity involves draining the message loop of ready tasks.
// Doing so must happen outside of the main message loop, so when the time
// comes for an idle check, this class escapes the main message loop with a
// quit task. When this happens, app main should call |FinishIdleCheck| and
// then resume the main message loop if it returns |true|.
//
// TODO(rosswang): Remove this requirement if |RunUntilIdle| is ever supported
// from within an outer message loop.
bool FinishIdleCheck();
// Waits until the app has reached a steady state such that no further
// activity will occur unless acted upon from the outside:
// * when there are no ready tasks in the message loop; delayed tasks are not
// included
// * when no outstanding copies of |ActivityToken|s created by
// |RegisterOngoingActivity| are held by the instrumented app
void WaitUntilIdle(fit::closure callback);
private:
// Idle checks must be performed outside of the main message loop, so
// |PostIdleCheck| escapes the main message loop with a quit task. See
// |FinishIdleCheck| for more information.
void PostIdleCheck();
async::Loop* loop_{};
std::vector<fit::closure> callbacks_;
Activity* activity_ = nullptr;
bool idle_check_pending_ = false;
fxl::WeakPtrFactory<IdleWaiter> weak_ptr_factory_;
};
} // namespace util
#endif // PERIDOT_LIB_UTIL_IDLE_WAITER_H_