blob: 70f0ac970382262a0932d376412c809389013589 [file] [log] [blame]
// Copyright 2020 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_CAMERA_LIB_HANGING_GET_HELPER_HANGING_GET_HELPER_H_
#define SRC_CAMERA_LIB_HANGING_GET_HELPER_HANGING_GET_HELPER_H_
#include <lib/fit/function.h>
#include <zircon/assert.h>
#include <optional>
#include <type_traits>
namespace camera {
// A helper for implementing the "hanging get" pattern in fidl protocols. For increased client
// simplicity, the helper invokes callbacks only when the most recent value differs from the
// previously sent one, defined by a caller-provided equality function. The helper is also usable
// with non-movable types, for which equality with previously sent values is assumed false.
// TODO(fxbug.dev/47611): convert to generic fuchsia lib
template <class T, class Compare = std::equal_to<T>, bool = std::is_copy_constructible<T>::value>
class HangingGetHelper;
template <class T, class Compare>
class HangingGetHelper<T, Compare, true> {
public:
HangingGetHelper(Compare eq = Compare()) : eq_(std::move(eq)) {}
// Sets or updates the saved callback to the provided value. Returns true iff an existing callback
// was overwritten.
bool Get(fit::function<void(T)> callback) {
if (callback_) {
ZX_DEBUG_ASSERT(!pending_);
callback_ = std::move(callback);
return true;
}
if (pending_) {
ZX_DEBUG_ASSERT(!callback_);
callback(pending_.value());
sent_ = pending_;
pending_ = std::nullopt;
return false;
}
callback_ = std::move(callback);
return false;
}
// Sets or updates the pending value to the provided value if it differs from a previously sent
// value. Returns true iff an existing value was overwritten.
bool Set(T value) {
if ((sent_ && eq_(sent_.value(), value)) || (pending_ && eq_(pending_.value(), value))) {
return false;
}
if (pending_) {
ZX_DEBUG_ASSERT(!callback_);
pending_ = value;
return true;
}
if (callback_) {
ZX_DEBUG_ASSERT(!pending_);
callback_(value);
callback_ = nullptr;
sent_ = value;
return false;
}
pending_ = value;
return false;
}
private:
fit::function<void(T)> callback_;
std::optional<T> pending_;
std::optional<T> sent_;
Compare eq_;
};
template <class T, class Compare>
class HangingGetHelper<T, Compare, false> {
public:
// Sets or updates the saved callback to the provided value. Returns true iff an existing callback
// was overwritten.
bool Get(fit::function<void(T)> callback) {
if (callback_) {
ZX_DEBUG_ASSERT(!pending_);
callback_ = std::move(callback);
return true;
}
if (pending_) {
ZX_DEBUG_ASSERT(!callback_);
callback(std::move(pending_.value()));
pending_ = std::nullopt;
return false;
}
callback_ = std::move(callback);
return false;
}
// Sets or updates the pending value to the provided value. Returns true iff an existing value was
// overwritten.
bool Set(T value) {
if (pending_) {
ZX_DEBUG_ASSERT(!callback_);
pending_ = std::move(value);
return true;
}
if (callback_) {
ZX_DEBUG_ASSERT(!pending_);
callback_(std::move(value));
callback_ = nullptr;
return false;
}
pending_ = std::move(value);
return false;
}
private:
fit::function<void(T)> callback_;
std::optional<T> pending_;
};
} // namespace camera
#endif // SRC_CAMERA_LIB_HANGING_GET_HELPER_HANGING_GET_HELPER_H_