123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149 |
- /*
- * Copyright (c) 2020, the SerenityOS developers.
- *
- * SPDX-License-Identifier: BSD-2-Clause
- */
- #include <Kernel/Devices/AsyncDeviceRequest.h>
- #include <Kernel/Devices/Device.h>
- namespace Kernel {
- AsyncDeviceRequest::AsyncDeviceRequest(Device& device)
- : m_device(device)
- , m_process(Process::current())
- {
- }
- AsyncDeviceRequest::~AsyncDeviceRequest()
- {
- {
- SpinlockLocker lock(m_lock);
- VERIFY(is_completed_result(m_result));
- VERIFY(m_sub_requests_pending.is_empty());
- }
- // We should not need any locking here anymore. The destructor should
- // only be called until either wait() or cancel() (once implemented) returned.
- // At that point no sub-request should be adding more requests and all
- // sub-requests should be completed (either succeeded, failed, or cancelled).
- // Which means there should be no more pending sub-requests and the
- // entire AsyncDeviceRequest hierarchy should be immutable.
- while (!m_sub_requests_complete.is_empty()) {
- // Note: sub_request is ref-counted, and we use this specific pattern
- // to allow make sure the refcount is dropped properly.
- auto sub_request = m_sub_requests_complete.take_first();
- VERIFY(is_completed_result(sub_request->m_result)); // Shouldn't need any locking anymore
- VERIFY(sub_request->m_parent_request == this);
- sub_request->m_parent_request = nullptr;
- }
- }
- void AsyncDeviceRequest::request_finished()
- {
- if (m_parent_request)
- m_parent_request->sub_request_finished(*this);
- // Trigger processing the next request
- m_device.process_next_queued_request({}, *this);
- // Wake anyone who may be waiting
- m_queue.wake_all();
- }
- auto AsyncDeviceRequest::wait(Time* timeout) -> RequestWaitResult
- {
- VERIFY(!m_parent_request);
- auto request_result = get_request_result();
- if (is_completed_result(request_result))
- return { request_result, Thread::BlockResult::NotBlocked };
- auto wait_result = m_queue.wait_on(Thread::BlockTimeout(false, timeout), name());
- return { get_request_result(), wait_result };
- }
- auto AsyncDeviceRequest::get_request_result() const -> RequestResult
- {
- SpinlockLocker lock(m_lock);
- return m_result;
- }
- void AsyncDeviceRequest::add_sub_request(NonnullRefPtr<AsyncDeviceRequest> sub_request)
- {
- // Sub-requests cannot be for the same device
- VERIFY(&m_device != &sub_request->m_device);
- VERIFY(sub_request->m_parent_request == nullptr);
- sub_request->m_parent_request = this;
- SpinlockLocker lock(m_lock);
- VERIFY(!is_completed_result(m_result));
- m_sub_requests_pending.append(sub_request);
- if (m_result == Started)
- sub_request->do_start(move(lock));
- }
- void AsyncDeviceRequest::sub_request_finished(AsyncDeviceRequest& sub_request)
- {
- bool all_completed;
- {
- SpinlockLocker lock(m_lock);
- VERIFY(m_result == Started);
- if (m_sub_requests_pending.contains(sub_request)) {
- // Note: append handles removing from any previous intrusive list internally.
- m_sub_requests_complete.append(sub_request);
- }
- all_completed = m_sub_requests_pending.is_empty();
- if (all_completed) {
- // Aggregate any errors
- bool any_failures = false;
- bool any_memory_faults = false;
- for (auto& com_sub_request : m_sub_requests_complete) {
- auto sub_result = com_sub_request.get_request_result();
- VERIFY(is_completed_result(sub_result));
- switch (sub_result) {
- case Failure:
- any_failures = true;
- break;
- case MemoryFault:
- any_memory_faults = true;
- break;
- default:
- break;
- }
- if (any_failures && any_memory_faults)
- break; // Stop checking if all error conditions were found
- }
- if (any_failures)
- m_result = Failure;
- else if (any_memory_faults)
- m_result = MemoryFault;
- else
- m_result = Success;
- }
- }
- if (all_completed)
- request_finished();
- }
- void AsyncDeviceRequest::complete(RequestResult result)
- {
- VERIFY(result == Success || result == Failure || result == MemoryFault);
- ScopedCritical critical;
- {
- SpinlockLocker lock(m_lock);
- VERIFY(m_result == Started);
- m_result = result;
- }
- if (Processor::current().in_irq()) {
- ref(); // Make sure we don't get freed
- Processor::deferred_call_queue([this]() {
- request_finished();
- unref();
- });
- } else {
- request_finished();
- }
- }
- }
|