| // Copyright 2023 Google Inc. All Rights Reserved. |
| // |
| // 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. |
| |
| #include "async_loop.h" |
| |
| #include "metrics.h" // For GetTimeMillis() |
| #include "util.h" |
| |
| #ifdef _WIN32 |
| #include "async_loop-win32.h" |
| #else |
| #include "async_loop-posix.h" |
| #endif |
| |
| // static |
| std::string AsyncErrorToString(AsyncError error) { |
| return std::string(strerror(error)); |
| } |
| |
| static int64_t AsyncLoopGlobalClock() { |
| static bool init = false; |
| static int64_t start_ms = 0; |
| int64_t result = GetTimeMillis(); |
| if (!init) { |
| start_ms = result; |
| init = true; |
| } |
| return result - start_ms; |
| } |
| |
| // Global instance. |
| static std::unique_ptr<AsyncLoop> s_loop; |
| |
| // static |
| AsyncLoop& AsyncLoop::Get() { |
| AsyncLoop* loop = s_loop.get(); |
| if (!loop) { |
| loop = new AsyncLoop(); |
| s_loop.reset(loop); |
| } |
| return *loop; |
| } |
| |
| // static |
| std::unique_ptr<AsyncLoop> AsyncLoop::CreateLocal() { |
| return std::unique_ptr<AsyncLoop>(new AsyncLoop()); |
| } |
| |
| void AsyncLoop::Reset() { |
| impl_.reset(new AsyncLoop::Impl()); |
| if (interrupt_catcher_count_ > 0) |
| impl_->EnableInterruptCatcher(); |
| } |
| |
| // static |
| void AsyncLoop::ResetForTesting() { |
| AsyncLoop* loop = s_loop.get(); |
| if (loop) |
| loop->Reset(); |
| } |
| |
| AsyncLoop::AsyncLoop() : impl_(new AsyncLoop::Impl()) {} |
| |
| AsyncLoop::~AsyncLoop() = default; |
| |
| // static |
| int64_t AsyncLoop::NowMs() { |
| if (clock_) |
| return (*clock_)(); |
| else |
| return AsyncLoopGlobalClock(); |
| } |
| |
| AsyncLoop::Clock* AsyncLoop::ChangeInternalClock(Clock* clock) { |
| Clock* result = clock_; |
| clock_ = clock; |
| return result; |
| } |
| |
| AsyncLoop::ExitStatus AsyncLoop::RunOnce(int64_t timeout_ms) { |
| return impl_->RunOnce(timeout_ms, *this); |
| } |
| |
| void AsyncLoop::ClearInterrupt() { |
| impl_->ClearInterrupt(); |
| } |
| |
| #ifndef _WIN32 |
| int AsyncLoop::GetInterruptSignal() const { |
| return impl_->GetInterruptSignal(); |
| } |
| |
| sigset_t AsyncLoop::GetOldSignalMask() const { |
| return impl_->GetOldSignalMask(); |
| } |
| #endif // !_WIN32 |
| |
| AsyncLoop::RunUntilState::RunUntilState(int64_t timeout_ms) |
| : timeout_ms_(timeout_ms) {} |
| |
| bool AsyncLoop::RunUntilState::LoopAgain(AsyncLoop& async_loop) { |
| // Initialize expiration_ms_ the first time this method is called. |
| int64_t expiration_ms = -1; |
| if (timeout_ms_ >= 0) |
| expiration_ms = async_loop.NowMs() + timeout_ms_; |
| |
| status_ = async_loop.RunOnce(timeout_ms_); |
| if (status_ != ExitSuccess) { |
| // Either an interrupt, timeout or idle exit. |
| // This is the end of the loop. |
| return false; |
| } |
| |
| if (timeout_ms_ < 0) { |
| // Since no timeout was specified, just loop again |
| // after an async event. |
| return true; |
| } |
| |
| // Adjust the timeout for the next invocation. |
| timeout_ms_ = expiration_ms - async_loop.NowMs(); |
| if (timeout_ms_ >= 0) { |
| // There is still time left, so loop again. |
| return true; |
| } |
| |
| // There is no time left, stop the loop reporting |
| // a real timeout. |
| timeout_ms_ = 0; |
| status_ = ExitTimeout; |
| return false; |
| } |
| |
| void AsyncLoop::ChangeInterruptCatcher(bool increment) { |
| if (increment) { |
| if (++interrupt_catcher_count_ == 1) |
| impl_->EnableInterruptCatcher(); |
| } else { |
| if (interrupt_catcher_count_ <= 0) |
| Fatal("Unbalanced ChangeInterruptCatcher() calls"); |
| if (--interrupt_catcher_count_ == 0) |
| impl_->DisableInterruptCatcher(); |
| } |
| } |
| |
| /////////////////////////////////////////////////////////////////////////// |
| /// |
| /// AsyncHandle |
| /// |
| |
| void AsyncLoop::AttachHandle(AsyncHandleImpl* handle) { |
| impl_->AttachHandle(handle); |
| } |
| |
| void AsyncLoop::DetachHandle(AsyncHandleImpl* handle) { |
| impl_->DetachHandle(handle); |
| } |
| |
| void AsyncLoop::UpdateHandle(AsyncHandleImpl* handle) { |
| impl_->UpdateHandle(handle); |
| } |
| |
| void AsyncLoop::CancelHandle(AsyncHandleImpl* handle) { |
| impl_->CancelHandle(handle); |
| } |
| |
| // static |
| std::unique_ptr<AsyncHandle> AsyncHandle::Create( |
| ScopedHandle handle, AsyncLoop& async_loop, |
| AsyncHandle::Callback&& callback) { |
| return std::make_unique<AsyncHandleImpl>(std::move(handle), async_loop, |
| std::move(callback)); |
| } |
| |
| /////////////////////////////////////////////////////////////////////////// |
| /// |
| /// AsyncTimer |
| /// |
| |
| void AsyncLoop::AttachTimer(AsyncTimerImpl* timer) { |
| impl_->timers().AttachTimer(timer); |
| } |
| |
| void AsyncLoop::DetachTimer(AsyncTimerImpl* timer) { |
| impl_->timers().DetachTimer(timer); |
| } |
| |
| void AsyncLoop::UpdateTimer(AsyncTimerImpl* timer) { |
| impl_->timers().UpdateTimer(timer); |
| } |
| |
| // static |
| std::unique_ptr<AsyncTimer> AsyncTimer::CreateWithExpiration( |
| int64_t expiration_ms, AsyncLoop& async_loop, |
| AsyncTimer::Callback&& callback) { |
| auto timer = |
| std::make_unique<AsyncTimerImpl>(async_loop, std::move(callback)); |
| timer->SetExpirationMs(expiration_ms); |
| return timer; |
| } |
| |
| // static |
| std::unique_ptr<AsyncTimer> AsyncTimer::CreateWithDuration( |
| int64_t duration_ms, AsyncLoop& async_loop, |
| AsyncTimer::Callback&& callback) { |
| auto timer = |
| std::make_unique<AsyncTimerImpl>(async_loop, std::move(callback)); |
| timer->SetDurationMs(duration_ms); |
| return timer; |
| } |
| |
| /////////////////////////////////////////////////////////////////////////// |
| /// |
| /// AsyncSigChild |
| /// |
| #ifndef _WIN32 |
| void AsyncLoop::SetSigChildCallback(AsyncSigChild::Callback&& cb) { |
| impl_->SetSigChildCallback(std::move(cb)); |
| } |
| |
| void AsyncLoop::ClearSigChildCallback() { |
| impl_->ClearSigChildCallback(); |
| } |
| |
| // static |
| std::unique_ptr<AsyncSigChild> AsyncSigChild::Create(AsyncLoop& async_loop, |
| Callback&& callback) { |
| return std::make_unique<AsyncSigChildImpl>(async_loop, std::move(callback)); |
| } |
| #endif // !_WIN32 |
| |
| /////////////////////////////////////////////////////////////////////////// |
| /// |
| /// AsyncFdReady |
| /// |
| #ifndef _WIN32 |
| void AsyncLoop::AttachFdReady(AsyncFdReadyImpl* handle) { |
| impl_->AttachFdReady(handle); |
| } |
| |
| void AsyncLoop::DetachFdReady(AsyncFdReadyImpl* handle) { |
| impl_->DetachFdReady(handle); |
| } |
| |
| void AsyncLoop::UpdateFdReady(AsyncFdReadyImpl* handle) { |
| impl_->UpdateFdReady(handle); |
| } |
| |
| std::unique_ptr<AsyncFdReadyFlags> AsyncFdReadyFlags::Create( |
| AsyncLoop& async_loop, int fd) { |
| return std::make_unique<AsyncFdReadyImpl>(async_loop, fd); |
| } |
| #endif // !_WIN32 |