blob: fd6f74e8e94a6545ea5e2c3f02b7a0a832d75246 [file] [log] [blame]
// Copyright 2020 Google LLC
//
// 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
//
// https://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 LOCATION_NEARBY_CPP_PLATFORM_PUBLIC_SETTABLE_FUTURE_H_
#define LOCATION_NEARBY_CPP_PLATFORM_PUBLIC_SETTABLE_FUTURE_H_
#include <utility>
#include "platform/public/condition_variable.h"
#include "platform/public/mutex.h"
#include "platform/public/mutex_lock.h"
#include "platform/public/system_clock.h"
namespace location {
namespace nearby {
template <typename T>
class SettableFuture : public api::SettableFuture<T> {
public:
SettableFuture() = default;
~SettableFuture() override = default;
bool Set(T value) override {
MutexLock lock(&mutex_);
if (!done_) {
value_ = std::move(value);
done_ = true;
exception_ = {Exception::kSuccess};
completed_.Notify();
InvokeAllLocked();
return true;
}
return false;
}
void AddListener(Runnable runnable, api::Executor* executor) override {
MutexLock lock(&mutex_);
if (done_) {
executor->Execute(std::move(runnable));
} else {
listeners_.emplace_back(std::make_pair(executor, std::move(runnable)));
}
}
bool IsSet() const {
MutexLock lock(&mutex_);
return done_;
}
bool SetException(Exception exception) override {
MutexLock lock(&mutex_);
return SetExceptionLocked(exception);
}
ExceptionOr<T> Get() override {
MutexLock lock(&mutex_);
while (!done_) {
completed_.Wait();
}
return exception_.value != Exception::kSuccess
? ExceptionOr<T>{exception_.value}
: ExceptionOr<T>{value_};
}
ExceptionOr<T> Get(absl::Duration timeout) override {
MutexLock lock(&mutex_);
while (!done_) {
absl::Time start_time = SystemClock::ElapsedRealtime();
if (completed_.Wait(timeout).Raised(Exception::kInterrupted)) {
SetExceptionLocked({Exception::kInterrupted});
break;
}
absl::Duration spent = SystemClock::ElapsedRealtime() - start_time;
if (spent < timeout) {
timeout -= spent;
} else if (!done_) {
SetExceptionLocked({Exception::kTimeout});
break;
}
}
return exception_.value != Exception::kSuccess
? ExceptionOr<T>{exception_.value}
: ExceptionOr<T>{value_};
}
private:
bool SetExceptionLocked(Exception exception) {
if (!done_) {
exception_ = exception.value != Exception::kSuccess
? exception
: Exception{Exception::kFailed};
done_ = true;
completed_.Notify();
InvokeAllLocked();
}
return true;
}
void InvokeAllLocked() {
for (auto& item : listeners_) {
item.first->Execute(std::move(item.second));
}
listeners_.clear();
}
mutable Mutex mutex_;
ConditionVariable completed_{&mutex_};
std::vector<std::pair<api::Executor*, std::function<void()>>> listeners_;
bool done_{false};
T value_;
Exception exception_{Exception::kFailed};
};
} // namespace nearby
} // namespace location
#endif // LOCATION_NEARBY_CPP_PLATFORM_PUBLIC_SETTABLE_FUTURE_H_