1 // Copyright 2012 The Chromium Authors 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "net/disk_cache/blockfile/in_flight_io.h" 6 7 #include "base/functional/bind.h" 8 #include "base/location.h" 9 #include "base/synchronization/waitable_event.h" 10 #include "base/task/sequenced_task_runner.h" 11 #include "base/task/single_thread_task_runner.h" 12 #include "base/threading/thread_restrictions.h" 13 14 namespace disk_cache { 15 BackgroundIO(InFlightIO * controller)16BackgroundIO::BackgroundIO(InFlightIO* controller) 17 : io_completed_(base::WaitableEvent::ResetPolicy::MANUAL, 18 base::WaitableEvent::InitialState::NOT_SIGNALED), 19 controller_(controller) {} 20 21 // Runs on the primary thread. OnIOSignalled()22void BackgroundIO::OnIOSignalled() { 23 if (controller_) { 24 did_notify_controller_io_signalled_ = true; 25 controller_->InvokeCallback(this, false); 26 } 27 } 28 Cancel()29void BackgroundIO::Cancel() { 30 // controller_ may be in use from the background thread at this time. 31 base::AutoLock lock(controller_lock_); 32 DCHECK(controller_); 33 controller_ = nullptr; 34 } 35 ClearController()36void BackgroundIO::ClearController() { 37 controller_ = nullptr; 38 } 39 40 BackgroundIO::~BackgroundIO() = default; 41 42 // --------------------------------------------------------------------------- 43 InFlightIO()44InFlightIO::InFlightIO() 45 : callback_task_runner_(base::SingleThreadTaskRunner::GetCurrentDefault()) { 46 } 47 48 InFlightIO::~InFlightIO() = default; 49 50 // Runs on the background thread. NotifyController()51void BackgroundIO::NotifyController() { 52 base::AutoLock lock(controller_lock_); 53 if (controller_) 54 controller_->OnIOComplete(this); 55 } 56 WaitForPendingIO()57void InFlightIO::WaitForPendingIO() { 58 while (!io_list_.empty()) { 59 // Block the current thread until all pending IO completes. 60 auto it = io_list_.begin(); 61 InvokeCallback(it->get(), true); 62 } 63 } 64 DropPendingIO()65void InFlightIO::DropPendingIO() { 66 while (!io_list_.empty()) { 67 auto it = io_list_.begin(); 68 BackgroundIO* operation = it->get(); 69 operation->Cancel(); 70 DCHECK(io_list_.find(operation) != io_list_.end()); 71 io_list_.erase(base::WrapRefCounted(operation)); 72 } 73 } 74 75 // Runs in a background sequence. OnIOComplete(BackgroundIO * operation)76void InFlightIO::OnIOComplete(BackgroundIO* operation) { 77 #if DCHECK_IS_ON() 78 if (callback_task_runner_->RunsTasksInCurrentSequence()) { 79 DCHECK(single_thread_ || !running_); 80 single_thread_ = true; 81 } 82 #endif 83 84 callback_task_runner_->PostTask( 85 FROM_HERE, base::BindOnce(&BackgroundIO::OnIOSignalled, operation)); 86 operation->io_completed()->Signal(); 87 } 88 89 // Runs on the primary thread. InvokeCallback(BackgroundIO * operation,bool cancel_task)90void InFlightIO::InvokeCallback(BackgroundIO* operation, bool cancel_task) { 91 { 92 // http://crbug.com/74623 93 base::ScopedAllowBaseSyncPrimitivesOutsideBlockingScope allow_wait; 94 operation->io_completed()->Wait(); 95 } 96 running_ = true; 97 98 if (cancel_task) 99 operation->Cancel(); 100 101 // Make sure that we remove the operation from the list before invoking the 102 // callback (so that a subsequent cancel does not invoke the callback again). 103 DCHECK(io_list_.find(operation) != io_list_.end()); 104 DCHECK(!operation->HasOneRef()); 105 io_list_.erase(base::WrapRefCounted(operation)); 106 OnOperationComplete(operation, cancel_task); 107 } 108 109 // Runs on the primary thread. OnOperationPosted(BackgroundIO * operation)110void InFlightIO::OnOperationPosted(BackgroundIO* operation) { 111 DCHECK(callback_task_runner_->RunsTasksInCurrentSequence()); 112 io_list_.insert(base::WrapRefCounted(operation)); 113 } 114 115 } // namespace disk_cache 116