| // Copyright 2016 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. |
| |
| #include "topaz/lib/deprecated_loop/message_loop.h" |
| |
| #include <utility> |
| |
| #include <lib/async/cpp/task.h> |
| #include <zircon/syscalls.h> |
| |
| #include "lib/fxl/logging.h" |
| |
| namespace deprecated_loop { |
| namespace { |
| |
| thread_local MessageLoop* g_current; |
| |
| } // namespace |
| |
| MessageLoop::MessageLoop() |
| : MessageLoop(fxl::MakeRefCounted<internal::IncomingTaskQueue>()) {} |
| |
| MessageLoop::MessageLoop( |
| fxl::RefPtr<internal::IncomingTaskQueue> incoming_tasks) |
| : loop_config_{.make_default_for_current_thread = true, |
| .epilogue = &MessageLoop::Epilogue, |
| .data = this}, |
| loop_(&loop_config_), |
| task_runner_(std::move(incoming_tasks)) { |
| FXL_DCHECK(!g_current) << "At most one message loop per thread."; |
| g_current = this; |
| |
| MessageLoop::incoming_tasks()->InitDelegate(this); |
| } |
| |
| MessageLoop::~MessageLoop() { |
| FXL_DCHECK(g_current == this) |
| << "Message loops must be destroyed on their own threads."; |
| |
| loop_.Shutdown(); |
| |
| incoming_tasks()->ClearDelegate(); |
| |
| g_current = nullptr; |
| } |
| |
| MessageLoop* MessageLoop::GetCurrent() { return g_current; } |
| |
| void MessageLoop::PostTask(fxl::Closure task, fxl::TimePoint target_time) { |
| zx_status_t status = async::PostTaskForTime( |
| loop_.dispatcher(), [task = std::move(task)] { task(); }, |
| zx::time(target_time.ToEpochDelta().ToNanoseconds())); |
| FXL_CHECK(status == ZX_OK || status == ZX_ERR_BAD_STATE) |
| << "Failed to post task: status=" << status; |
| } |
| |
| void MessageLoop::Run(bool until_idle) { |
| FXL_DCHECK(g_current == this); |
| |
| FXL_CHECK(!is_running_) << "Cannot run a nested message loop."; |
| is_running_ = true; |
| |
| zx_status_t status = until_idle ? loop_.RunUntilIdle() : loop_.Run(); |
| FXL_CHECK(status == ZX_OK || status == ZX_ERR_CANCELED) |
| << "Loop stopped abnormally: status=" << status; |
| |
| status = loop_.ResetQuit(); |
| FXL_DCHECK(status == ZX_OK) |
| << "Failed to reset quit state: status=" << status; |
| |
| FXL_DCHECK(is_running_); |
| is_running_ = false; |
| } |
| |
| void MessageLoop::Run() { Run(false); } |
| |
| void MessageLoop::RunUntilIdle() { Run(true); } |
| |
| void MessageLoop::QuitNow() { |
| FXL_DCHECK(g_current == this); |
| |
| if (is_running_) |
| loop_.Quit(); |
| } |
| |
| void MessageLoop::PostQuitTask() { |
| task_runner()->PostTask([this]() { QuitNow(); }); |
| } |
| |
| bool MessageLoop::RunsTasksOnCurrentThread() { return g_current == this; } |
| |
| void MessageLoop::SetAfterTaskCallback(fxl::Closure callback) { |
| FXL_DCHECK(g_current == this); |
| |
| after_task_callback_ = std::move(callback); |
| } |
| |
| void MessageLoop::ClearAfterTaskCallback() { |
| FXL_DCHECK(g_current == this); |
| |
| after_task_callback_ = fxl::Closure(); |
| } |
| |
| void MessageLoop::Epilogue(async_loop_t*, void* data) { |
| auto loop = static_cast<MessageLoop*>(data); |
| if (loop->after_task_callback_) |
| loop->after_task_callback_(); |
| } |
| |
| } // namespace deprecated_loop |