Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(710)

Side by Side Diff: device/hid/hid_connection_win.cc

Issue 2573683002: Fix leak of HID transfers and handles on Windows. (Closed)
Patch Set: Created 4 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « device/hid/hid_connection_win.h ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2014 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "device/hid/hid_connection_win.h" 5 #include "device/hid/hid_connection_win.h"
6 6
7 #include <cstring> 7 #include <cstring>
8 #include <utility> 8 #include <utility>
9 9
10 #include "base/bind.h" 10 #include "base/bind.h"
11 #include "base/files/file.h" 11 #include "base/files/file.h"
12 #include "base/macros.h" 12 #include "base/macros.h"
13 #include "base/memory/ptr_util.h"
13 #include "base/message_loop/message_loop.h" 14 #include "base/message_loop/message_loop.h"
14 #include "base/numerics/safe_conversions.h" 15 #include "base/numerics/safe_conversions.h"
15 #include "base/win/object_watcher.h" 16 #include "base/win/object_watcher.h"
16 #include "components/device_event_log/device_event_log.h" 17 #include "components/device_event_log/device_event_log.h"
17 18
18 #define INITGUID 19 #define INITGUID
19 20
20 #include <windows.h> 21 #include <windows.h>
21 #include <hidclass.h> 22 #include <hidclass.h>
22 23
23 extern "C" { 24 extern "C" {
24 #include <hidsdi.h> 25 #include <hidsdi.h>
25 } 26 }
26 27
27 #include <setupapi.h> 28 #include <setupapi.h>
28 #include <winioctl.h> 29 #include <winioctl.h>
29 30
30 namespace device { 31 namespace device {
31 32
32 struct PendingHidTransfer : public base::RefCounted<PendingHidTransfer>, 33 class PendingHidTransfer : public base::win::ObjectWatcher::Delegate,
33 public base::win::ObjectWatcher::Delegate, 34 public base::MessageLoop::DestructionObserver {
34 public base::MessageLoop::DestructionObserver { 35 public:
35 typedef base::Callback<void(PendingHidTransfer*, bool)> Callback; 36 typedef base::OnceCallback<void(PendingHidTransfer*, bool)> Callback;
36 37
37 PendingHidTransfer(scoped_refptr<net::IOBuffer> buffer, 38 PendingHidTransfer(scoped_refptr<net::IOBuffer> buffer, Callback callback);
38 const Callback& callback); 39 ~PendingHidTransfer() override;
39 40
40 void TakeResultFromWindowsAPI(BOOL result); 41 void TakeResultFromWindowsAPI(BOOL result);
41 42
42 OVERLAPPED* GetOverlapped() { return &overlapped_; } 43 OVERLAPPED* GetOverlapped() { return &overlapped_; }
43 44
44 // Implements base::win::ObjectWatcher::Delegate. 45 // Implements base::win::ObjectWatcher::Delegate.
45 void OnObjectSignaled(HANDLE object) override; 46 void OnObjectSignaled(HANDLE object) override;
46 47
47 // Implements base::MessageLoop::DestructionObserver 48 // Implements base::MessageLoop::DestructionObserver
48 void WillDestroyCurrentMessageLoop() override; 49 void WillDestroyCurrentMessageLoop() override;
49 50
51 private:
50 // The buffer isn't used by this object but it's important that a reference 52 // The buffer isn't used by this object but it's important that a reference
51 // to it is held until the transfer completes. 53 // to it is held until the transfer completes.
52 scoped_refptr<net::IOBuffer> buffer_; 54 scoped_refptr<net::IOBuffer> buffer_;
53 Callback callback_; 55 Callback callback_;
54 OVERLAPPED overlapped_; 56 OVERLAPPED overlapped_;
55 base::win::ScopedHandle event_; 57 base::win::ScopedHandle event_;
56 base::win::ObjectWatcher watcher_; 58 base::win::ObjectWatcher watcher_;
57 59
58 private:
59 friend class base::RefCounted<PendingHidTransfer>;
60
61 ~PendingHidTransfer() override;
62
63 DISALLOW_COPY_AND_ASSIGN(PendingHidTransfer); 60 DISALLOW_COPY_AND_ASSIGN(PendingHidTransfer);
64 }; 61 };
65 62
66 PendingHidTransfer::PendingHidTransfer( 63 PendingHidTransfer::PendingHidTransfer(scoped_refptr<net::IOBuffer> buffer,
67 scoped_refptr<net::IOBuffer> buffer, 64 PendingHidTransfer::Callback callback)
68 const PendingHidTransfer::Callback& callback)
69 : buffer_(buffer), 65 : buffer_(buffer),
70 callback_(callback), 66 callback_(std::move(callback)),
71 event_(CreateEvent(NULL, FALSE, FALSE, NULL)) { 67 event_(CreateEvent(NULL, FALSE, FALSE, NULL)) {
72 memset(&overlapped_, 0, sizeof(OVERLAPPED)); 68 memset(&overlapped_, 0, sizeof(OVERLAPPED));
73 overlapped_.hEvent = event_.Get(); 69 overlapped_.hEvent = event_.Get();
74 } 70 }
75 71
76 PendingHidTransfer::~PendingHidTransfer() { 72 PendingHidTransfer::~PendingHidTransfer() {
77 base::MessageLoop::current()->RemoveDestructionObserver(this); 73 base::MessageLoop::current()->RemoveDestructionObserver(this);
74 if (callback_)
75 std::move(callback_).Run(this, false);
78 } 76 }
79 77
80 void PendingHidTransfer::TakeResultFromWindowsAPI(BOOL result) { 78 void PendingHidTransfer::TakeResultFromWindowsAPI(BOOL result) {
81 if (result) { 79 if (result) {
82 callback_.Run(this, true); 80 std::move(callback_).Run(this, true);
83 } else if (GetLastError() == ERROR_IO_PENDING) { 81 } else if (GetLastError() == ERROR_IO_PENDING) {
84 base::MessageLoop::current()->AddDestructionObserver(this); 82 base::MessageLoop::current()->AddDestructionObserver(this);
85 AddRef();
86 watcher_.StartWatchingOnce(event_.Get(), this); 83 watcher_.StartWatchingOnce(event_.Get(), this);
87 } else { 84 } else {
88 HID_PLOG(EVENT) << "HID transfer failed"; 85 HID_PLOG(EVENT) << "HID transfer failed";
89 callback_.Run(this, false); 86 std::move(callback_).Run(this, false);
90 } 87 }
91 } 88 }
92 89
93 void PendingHidTransfer::OnObjectSignaled(HANDLE event_handle) { 90 void PendingHidTransfer::OnObjectSignaled(HANDLE event_handle) {
94 callback_.Run(this, true); 91 std::move(callback_).Run(this, true);
95 Release();
96 } 92 }
97 93
98 void PendingHidTransfer::WillDestroyCurrentMessageLoop() { 94 void PendingHidTransfer::WillDestroyCurrentMessageLoop() {
99 watcher_.StopWatching(); 95 watcher_.StopWatching();
100 callback_.Run(this, false); 96 std::move(callback_).Run(this, false);
101 } 97 }
102 98
103 HidConnectionWin::HidConnectionWin(scoped_refptr<HidDeviceInfo> device_info, 99 HidConnectionWin::HidConnectionWin(scoped_refptr<HidDeviceInfo> device_info,
104 base::win::ScopedHandle file) 100 base::win::ScopedHandle file)
105 : HidConnection(device_info) { 101 : HidConnection(device_info), file_(std::move(file)) {}
106 file_ = std::move(file);
107 }
108 102
109 HidConnectionWin::~HidConnectionWin() { 103 HidConnectionWin::~HidConnectionWin() {
104 DCHECK(!file_.IsValid());
105 DCHECK(transfers_.empty());
110 } 106 }
111 107
112 void HidConnectionWin::PlatformClose() { 108 void HidConnectionWin::PlatformClose() {
113 CancelIo(file_.Get()); 109 CancelIo(file_.Get());
110 file_.Close();
111 transfers_.clear();
114 } 112 }
115 113
116 void HidConnectionWin::PlatformRead( 114 void HidConnectionWin::PlatformRead(
117 const HidConnection::ReadCallback& callback) { 115 const HidConnection::ReadCallback& callback) {
118 // Windows will always include the report ID (including zero if report IDs 116 // Windows will always include the report ID (including zero if report IDs
119 // are not in use) in the buffer. 117 // are not in use) in the buffer.
120 scoped_refptr<net::IOBufferWithSize> buffer = new net::IOBufferWithSize( 118 scoped_refptr<net::IOBufferWithSize> buffer = new net::IOBufferWithSize(
121 base::checked_cast<int>(device_info()->max_input_report_size() + 1)); 119 base::checked_cast<int>(device_info()->max_input_report_size() + 1));
122 scoped_refptr<PendingHidTransfer> transfer(new PendingHidTransfer( 120 transfers_.push_back(base::MakeUnique<PendingHidTransfer>(
123 buffer, 121 buffer, base::BindOnce(&HidConnectionWin::OnReadComplete, this, buffer,
124 base::Bind(&HidConnectionWin::OnReadComplete, this, buffer, callback))); 122 callback)));
125 transfers_.insert(transfer); 123 transfers_.back()->TakeResultFromWindowsAPI(
126 transfer->TakeResultFromWindowsAPI( 124 ReadFile(file_.Get(), buffer->data(), static_cast<DWORD>(buffer->size()),
127 ReadFile(file_.Get(), 125 NULL, transfers_.back()->GetOverlapped()));
128 buffer->data(),
129 static_cast<DWORD>(buffer->size()),
130 NULL,
131 transfer->GetOverlapped()));
132 } 126 }
133 127
134 void HidConnectionWin::PlatformWrite(scoped_refptr<net::IOBuffer> buffer, 128 void HidConnectionWin::PlatformWrite(scoped_refptr<net::IOBuffer> buffer,
135 size_t size, 129 size_t size,
136 const WriteCallback& callback) { 130 const WriteCallback& callback) {
137 size_t expected_size = device_info()->max_output_report_size() + 1; 131 size_t expected_size = device_info()->max_output_report_size() + 1;
138 DCHECK(size <= expected_size); 132 DCHECK(size <= expected_size);
139 // The Windows API always wants either a report ID (if supported) or zero at 133 // The Windows API always wants either a report ID (if supported) or zero at
140 // the front of every output report and requires that the buffer size be equal 134 // the front of every output report and requires that the buffer size be equal
141 // to the maximum output report size supported by this collection. 135 // to the maximum output report size supported by this collection.
142 if (size < expected_size) { 136 if (size < expected_size) {
143 scoped_refptr<net::IOBuffer> tmp_buffer = new net::IOBuffer( 137 scoped_refptr<net::IOBuffer> tmp_buffer = new net::IOBuffer(
144 base::checked_cast<int>(expected_size)); 138 base::checked_cast<int>(expected_size));
145 memcpy(tmp_buffer->data(), buffer->data(), size); 139 memcpy(tmp_buffer->data(), buffer->data(), size);
146 memset(tmp_buffer->data() + size, 0, expected_size - size); 140 memset(tmp_buffer->data() + size, 0, expected_size - size);
147 buffer = tmp_buffer; 141 buffer = tmp_buffer;
148 size = expected_size; 142 size = expected_size;
149 } 143 }
150 scoped_refptr<PendingHidTransfer> transfer(new PendingHidTransfer( 144 transfers_.push_back(base::MakeUnique<PendingHidTransfer>(
151 buffer, base::Bind(&HidConnectionWin::OnWriteComplete, this, callback))); 145 buffer,
152 transfers_.insert(transfer); 146 base::BindOnce(&HidConnectionWin::OnWriteComplete, this, callback)));
153 transfer->TakeResultFromWindowsAPI(WriteFile(file_.Get(), 147 transfers_.back()->TakeResultFromWindowsAPI(
154 buffer->data(), 148 WriteFile(file_.Get(), buffer->data(), static_cast<DWORD>(size), NULL,
155 static_cast<DWORD>(size), 149 transfers_.back()->GetOverlapped()));
156 NULL,
157 transfer->GetOverlapped()));
158 } 150 }
159 151
160 void HidConnectionWin::PlatformGetFeatureReport(uint8_t report_id, 152 void HidConnectionWin::PlatformGetFeatureReport(uint8_t report_id,
161 const ReadCallback& callback) { 153 const ReadCallback& callback) {
162 // The first byte of the destination buffer is the report ID being requested. 154 // The first byte of the destination buffer is the report ID being requested.
163 scoped_refptr<net::IOBufferWithSize> buffer = new net::IOBufferWithSize( 155 scoped_refptr<net::IOBufferWithSize> buffer = new net::IOBufferWithSize(
164 base::checked_cast<int>(device_info()->max_feature_report_size() + 1)); 156 base::checked_cast<int>(device_info()->max_feature_report_size() + 1));
165 buffer->data()[0] = report_id; 157 buffer->data()[0] = report_id;
166 158
167 scoped_refptr<PendingHidTransfer> transfer(new PendingHidTransfer( 159 transfers_.push_back(base::MakeUnique<PendingHidTransfer>(
168 buffer, 160 buffer, base::BindOnce(&HidConnectionWin::OnReadFeatureComplete, this,
169 base::Bind( 161 buffer, callback)));
170 &HidConnectionWin::OnReadFeatureComplete, this, buffer, callback))); 162 transfers_.back()->TakeResultFromWindowsAPI(
171 transfers_.insert(transfer); 163 DeviceIoControl(file_.Get(), IOCTL_HID_GET_FEATURE, NULL, 0,
172 transfer->TakeResultFromWindowsAPI( 164 buffer->data(), static_cast<DWORD>(buffer->size()), NULL,
173 DeviceIoControl(file_.Get(), 165 transfers_.back()->GetOverlapped()));
174 IOCTL_HID_GET_FEATURE,
175 NULL,
176 0,
177 buffer->data(),
178 static_cast<DWORD>(buffer->size()),
179 NULL,
180 transfer->GetOverlapped()));
181 } 166 }
182 167
183 void HidConnectionWin::PlatformSendFeatureReport( 168 void HidConnectionWin::PlatformSendFeatureReport(
184 scoped_refptr<net::IOBuffer> buffer, 169 scoped_refptr<net::IOBuffer> buffer,
185 size_t size, 170 size_t size,
186 const WriteCallback& callback) { 171 const WriteCallback& callback) {
187 // The Windows API always wants either a report ID (if supported) or 172 // The Windows API always wants either a report ID (if supported) or
188 // zero at the front of every output report. 173 // zero at the front of every output report.
189 scoped_refptr<PendingHidTransfer> transfer(new PendingHidTransfer( 174 transfers_.push_back(base::MakeUnique<PendingHidTransfer>(
190 buffer, base::Bind(&HidConnectionWin::OnWriteComplete, this, callback))); 175 buffer,
191 transfer->TakeResultFromWindowsAPI( 176 base::BindOnce(&HidConnectionWin::OnWriteComplete, this, callback)));
192 DeviceIoControl(file_.Get(), 177 transfers_.back()->TakeResultFromWindowsAPI(
193 IOCTL_HID_SET_FEATURE, 178 DeviceIoControl(file_.Get(), IOCTL_HID_SET_FEATURE, buffer->data(),
194 buffer->data(), 179 static_cast<DWORD>(size), NULL, 0, NULL,
195 static_cast<DWORD>(size), 180 transfers_.back()->GetOverlapped()));
196 NULL,
197 0,
198 NULL,
199 transfer->GetOverlapped()));
200 } 181 }
201 182
202 void HidConnectionWin::OnReadComplete(scoped_refptr<net::IOBuffer> buffer, 183 void HidConnectionWin::OnReadComplete(scoped_refptr<net::IOBuffer> buffer,
203 const ReadCallback& callback, 184 const ReadCallback& callback,
204 PendingHidTransfer* transfer, 185 PendingHidTransfer* transfer_raw,
205 bool signaled) { 186 bool signaled) {
206 if (!signaled) { 187 if (!file_.IsValid()) {
207 callback.Run(false, NULL, 0); 188 callback.Run(false, nullptr, 0);
208 return; 189 return;
209 } 190 }
210 191
192 std::unique_ptr<PendingHidTransfer> transfer = UnlinkTransfer(transfer_raw);
211 DWORD bytes_transferred; 193 DWORD bytes_transferred;
212 if (GetOverlappedResult( 194 if (signaled && GetOverlappedResult(file_.Get(), transfer->GetOverlapped(),
213 file_.Get(), transfer->GetOverlapped(), &bytes_transferred, FALSE)) { 195 &bytes_transferred, FALSE)) {
214 CompleteRead(buffer, bytes_transferred, callback); 196 CompleteRead(buffer, bytes_transferred, callback);
215 } else { 197 } else {
216 HID_PLOG(EVENT) << "HID read failed"; 198 HID_PLOG(EVENT) << "HID read failed";
217 callback.Run(false, NULL, 0); 199 callback.Run(false, nullptr, 0);
218 } 200 }
219 } 201 }
220 202
221 void HidConnectionWin::OnReadFeatureComplete( 203 void HidConnectionWin::OnReadFeatureComplete(
222 scoped_refptr<net::IOBuffer> buffer, 204 scoped_refptr<net::IOBuffer> buffer,
223 const ReadCallback& callback, 205 const ReadCallback& callback,
224 PendingHidTransfer* transfer, 206 PendingHidTransfer* transfer_raw,
225 bool signaled) { 207 bool signaled) {
226 if (!signaled) { 208 if (!file_.IsValid()) {
227 callback.Run(false, NULL, 0); 209 callback.Run(false, nullptr, 0);
228 return; 210 return;
229 } 211 }
230 212
213 std::unique_ptr<PendingHidTransfer> transfer = UnlinkTransfer(transfer_raw);
231 DWORD bytes_transferred; 214 DWORD bytes_transferred;
232 if (GetOverlappedResult( 215 if (signaled && GetOverlappedResult(file_.Get(), transfer->GetOverlapped(),
233 file_.Get(), transfer->GetOverlapped(), &bytes_transferred, FALSE)) { 216 &bytes_transferred, FALSE)) {
234 callback.Run(true, buffer, bytes_transferred); 217 callback.Run(true, buffer, bytes_transferred);
235 } else { 218 } else {
236 HID_PLOG(EVENT) << "HID read failed"; 219 HID_PLOG(EVENT) << "HID read failed";
237 callback.Run(false, NULL, 0); 220 callback.Run(false, nullptr, 0);
238 } 221 }
239 } 222 }
240 223
241 void HidConnectionWin::OnWriteComplete(const WriteCallback& callback, 224 void HidConnectionWin::OnWriteComplete(const WriteCallback& callback,
242 PendingHidTransfer* transfer, 225 PendingHidTransfer* transfer_raw,
243 bool signaled) { 226 bool signaled) {
244 if (!signaled) { 227 if (!file_.IsValid()) {
245 callback.Run(false); 228 callback.Run(false);
246 return; 229 return;
247 } 230 }
248 231
232 std::unique_ptr<PendingHidTransfer> transfer = UnlinkTransfer(transfer_raw);
249 DWORD bytes_transferred; 233 DWORD bytes_transferred;
250 if (GetOverlappedResult( 234 if (signaled && GetOverlappedResult(file_.Get(), transfer->GetOverlapped(),
251 file_.Get(), transfer->GetOverlapped(), &bytes_transferred, FALSE)) { 235 &bytes_transferred, FALSE)) {
252 callback.Run(true); 236 callback.Run(true);
253 } else { 237 } else {
254 HID_PLOG(EVENT) << "HID write failed"; 238 HID_PLOG(EVENT) << "HID write failed";
255 callback.Run(false); 239 callback.Run(false);
256 } 240 }
257 } 241 }
258 242
243 std::unique_ptr<PendingHidTransfer> HidConnectionWin::UnlinkTransfer(
244 PendingHidTransfer* transfer) {
245 auto it = std::find_if(
246 transfers_.begin(), transfers_.end(),
247 [transfer](const std::unique_ptr<PendingHidTransfer>& this_transfer) {
248 return transfer == this_transfer.get();
249 });
250 DCHECK(it != transfers_.end());
251 std::unique_ptr<PendingHidTransfer> saved_transfer = std::move(*it);
252 transfers_.erase(it);
253 return saved_transfer;
254 }
255
259 } // namespace device 256 } // namespace device
OLDNEW
« no previous file with comments | « device/hid/hid_connection_win.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698