blob: 4162b9df42ceec04054df25bb3271cf42d64bbf6 [file] [log] [blame]
// Copyright 2021 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 SRC_SYS_FUZZING_COMMON_SYNC_WAIT_H_
#define SRC_SYS_FUZZING_COMMON_SYNC_WAIT_H_
#include <lib/fit/function.h>
#include <lib/sync/completion.h>
#include <lib/zx/time.h>
#include <atomic>
#include <string_view>
#include "src/lib/fxl/macros.h"
namespace fuzzing {
// Defines a function that takes a deadline and may return ZX_ERR_TIMED_OUT if that deadline is
// passed.
using Waiter = fit::function<zx_status_t(zx::time)>;
// Waiter functions
// Calls |waiter| with no deadline (i.e. the deadline is |zx::time::infinite|). Returns the result
// of calling |waiter|.
//
// If |waiter| completes within a configured threshold, no further action is taken. Otherwise, it
// logs a warning and logs an additional message if |waiter| eventually completes.
//
// Example:
//
// auto waiter = [](zx::time deadline){
// return channel.wait_one(ZX_CHANNEL_READABLE, deadline.get(), nullptr);
// };
// auto status = WaitFor("channel to become readable", &waiter);
//
zx_status_t WaitFor(const char* what, Waiter* waiter);
// Like |WaitFor|, but executes the waiter repeatedly after each |interval|. This allows creating
// |waiter|s that wait indefinitely for one condition, but can exit early by polling another.
zx_status_t PollFor(const char* what, Waiter* waiter, zx::duration interval);
// Configures the |threshold| after which |WaitFor| should log a warning. If the value is less than
// or equal to zero, logging is disabled. This should only be used for testing |WaitFor| and
// |SyncWait| themselves.
void SetThreshold(zx::duration threshold);
// Resets the |threshold| after which |WaitFor| should log a warning to the default value. This
// should only be used for testing |WaitFor| and |SyncWait| themselves.
void ResetThreshold();
// Equivalent to |SetThreshold(zx::duration(0)|. This disables all logging by |WaitFor|. Slow waits
// are logged by default for tests; other |main| functions may want to disable them since normal
// operation may involve slow waits (e.g. slow fuzzing iterations, waiting for the user, etc.).
void DisableSlowWaitLogging();
// This class is a thin wrapper around |sync_completion_t| that adds diagnostics when an indefinite
// wait exceeds a threshold, and ensures no waiters remain when the object goes out of scope.
class SyncWait final {
public:
SyncWait();
~SyncWait();
bool is_signaled() const { return sync_completion_signaled(&sync_); }
// Like |WaitFor| with a waiter that waits for this object to be |Signal|led.
//
// For example,
//
// SyncWait sync;
// std::thread t([&](){
// zx::nanosleep(zx::deadline_after(zx::min(1)));
// sync.Signal();
// });
// sync.WaitFor("event to happen");
//
// will log something similar to:
//
// WARNING: Still waiting for event to happen after 30 seconds...
// WARNING: Done waiting for event to happen after 60 seconds.
//
void WaitFor(const char* what);
// Returns ZX_OK if |Signal|ed before |duration| elapses, or ZX_ERR_TIMED_OUT.
zx_status_t TimedWait(zx::duration duration);
// Returns ZX_OK if |Signal|ed before |deadline| is reached, or ZX_ERR_TIMED_OUT.
zx_status_t WaitUntil(zx::time deadline);
void Signal();
void Reset();
private:
Waiter waiter_;
sync_completion_t sync_;
std::atomic<size_t> waiters_ = 0;
FXL_DISALLOW_COPY_ASSIGN_AND_MOVE(SyncWait);
};
} // namespace fuzzing
#endif // SRC_SYS_FUZZING_COMMON_SYNC_WAIT_H_