| // Copyright 2017 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 <async/wait_with_timeout.h> |
| |
| #include <zircon/assert.h> |
| |
| namespace async { |
| |
| WaitWithTimeout::WaitWithTimeout(zx_handle_t object, zx_signals_t trigger, |
| zx_time_t deadline, uint32_t flags) |
| : async_wait_t{{ASYNC_STATE_INIT}, &WaitWithTimeout::WaitHandler, object, trigger, flags, {}}, |
| async_task_t{{ASYNC_STATE_INIT}, &WaitWithTimeout::TimeoutHandler, deadline, 0u, {}} {} |
| |
| WaitWithTimeout::~WaitWithTimeout() = default; |
| |
| zx_status_t WaitWithTimeout::Begin(async_t* async) { |
| zx_status_t status = async_begin_wait(async, this); |
| if (status == ZX_OK && deadline() != ZX_TIME_INFINITE) { |
| status = async_post_task(async, this); |
| if (status != ZX_OK) { |
| zx_status_t cancel_status = async_cancel_wait(async, this); |
| ZX_DEBUG_ASSERT_MSG(cancel_status == ZX_OK, |
| "cancel_status=%d", cancel_status); |
| } |
| } |
| return status; |
| } |
| |
| zx_status_t WaitWithTimeout::Cancel(async_t* async) { |
| zx_status_t status = async_cancel_wait(async, this); |
| if (status == ZX_OK && deadline() != ZX_TIME_INFINITE) |
| status = async_cancel_task(async, this); |
| return status; |
| } |
| |
| async_wait_result_t WaitWithTimeout::WaitHandler(async_t* async, async_wait_t* wait, |
| zx_status_t status, |
| const zx_packet_signal_t* signal) { |
| auto self = static_cast<WaitWithTimeout*>(wait); |
| |
| // We must cancel the task before calling the handler in case it decides |
| // to destroy itself during execution. If this proves inefficient, we |
| // could make timeouts on waits a first class API. |
| if (self->deadline() != ZX_TIME_INFINITE) { |
| zx_status_t cancel_status = async_cancel_task(async, self); |
| ZX_DEBUG_ASSERT_MSG(cancel_status == ZX_OK, |
| "cancel_status=%d", cancel_status); |
| } |
| |
| async_wait_result_t result = self->handler_(async, status, signal); |
| |
| // If the result is ASYNC_WAIT_FINISHED then it's possible that the handler has |
| // already destroyed this object. So take care to only dereference it if the wait |
| // is still live. |
| if (result == ASYNC_WAIT_AGAIN && status == ZX_OK && |
| self->deadline() != ZX_TIME_INFINITE) { |
| zx_status_t post_status = async_post_task(async, self); |
| if (post_status != ZX_OK) { |
| // The loop is being destroyed. |
| ZX_DEBUG_ASSERT_MSG(post_status == ZX_ERR_BAD_STATE, |
| "post_status=%d", post_status); |
| return ASYNC_WAIT_FINISHED; |
| } |
| } |
| return result; |
| } |
| |
| async_task_result_t WaitWithTimeout::TimeoutHandler(async_t* async, async_task_t* task, |
| zx_status_t status) { |
| ZX_DEBUG_ASSERT(status == ZX_OK); |
| |
| auto self = static_cast<WaitWithTimeout*>(task); |
| zx_status_t cancel_status = async_cancel_wait(async, self); |
| ZX_DEBUG_ASSERT_MSG(cancel_status == ZX_OK, |
| "cancel_status=%d", cancel_status); |
| |
| async_wait_result_t result = self->handler_(async, ZX_ERR_TIMED_OUT, nullptr); |
| ZX_DEBUG_ASSERT(result == ASYNC_WAIT_FINISHED); |
| return ASYNC_TASK_FINISHED; |
| } |
| |
| } // namespace async |