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

Unified Diff: chrome/browser/usb/usb_service.cc

Issue 16316004: Separate usb device handle from usb device. (deprecate) (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Fix the threading mess Created 7 years, 6 months 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 side-by-side diff with in-line comments
Download patch
« no previous file with comments | « chrome/browser/usb/usb_service.h ('k') | chrome/browser/usb/usb_service_unittest.cc » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: chrome/browser/usb/usb_service.cc
diff --git a/chrome/browser/usb/usb_service.cc b/chrome/browser/usb/usb_service.cc
index 038786debf85048856500ce8f184578faade605f..e4c20b4dc39075aae0567c6a75f7572971fa0505 100644
--- a/chrome/browser/usb/usb_service.cc
+++ b/chrome/browser/usb/usb_service.cc
@@ -4,13 +4,17 @@
#include "chrome/browser/usb/usb_service.h"
+#include <cstring>
+#include <set>
#include <vector>
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/logging.h"
#include "base/stl_util.h"
-#include "chrome/browser/usb/usb_device.h"
+#include "base/synchronization/lock.h"
+#include "chrome/browser/usb/usb_device_handle.h"
+#include "content/public/browser/browser_thread.h"
#include "third_party/libusb/src/libusb/libusb.h"
#if defined(OS_CHROMEOS)
@@ -20,61 +24,197 @@
#endif // defined(OS_CHROMEOS)
using std::vector;
+using std::set;
+using content::BrowserThread;
+using base::PlatformThreadHandle;
+using base::RefCountedThreadSafe;
-// The UsbEventHandler works around a design flaw in the libusb interface. There
-// is currently no way to signal to libusb that any caller into one of the event
+// The UsbEventHandler dispatches USB events on separate thread. There is
+// currently no way to signal to libusb that any caller into one of the event
// handler calls should return without handling any events.
+//
+// This class manages the polling thread and assures the thread exits safely.
+// This class is only visible to UsbContext. UsbContext manages its life cycle.
class UsbEventHandler : public base::PlatformThread::Delegate {
- public:
- explicit UsbEventHandler(PlatformUsbContext context)
- : running_(true), context_(context) {
- base::PlatformThread::CreateNonJoinable(0, this);
- }
+ private:
+ friend class UsbContext;
+ friend struct base::DefaultDeleter<UsbEventHandler>;
- virtual ~UsbEventHandler() {}
+ explicit UsbEventHandler(PlatformUsbContext context);
+ virtual ~UsbEventHandler();
virtual void ThreadMain() OVERRIDE {
- base::PlatformThread::SetName("UsbEventHandler");
-
- DLOG(INFO) << "UsbEventHandler started.";
- while (running_) {
+ base::PlatformThread::SetName("UsbEventDispatcher");
+ VLOG(1) << "UsbEventDispatcher started.";
+ while (true) {
+ {
+ base::AutoLock running_guard(running_lock_);
+ if (!running_) break;
+ }
libusb_handle_events(context_);
}
- DLOG(INFO) << "UsbEventHandler shutting down.";
- libusb_exit(context_);
-
- delete this;
+ VLOG(1) << "UsbEventDispatcher shutting down.";
}
void Stop() {
- running_ = false;
+ {
+ base::AutoLock running_guard(running_lock_);
+ running_ = false;
+ }
+ // Send an event to interrupt the the polling.
+ libusb_send_event(context_);
+ // Wait for the thread to exit.
+ base::PlatformThread::Join(thread_handle);
}
+ volatile bool running_;
+ base::Lock running_lock_;
+ const PlatformUsbContext context_;
+ PlatformThreadHandle thread_handle;
+ DISALLOW_COPY_AND_ASSIGN(UsbEventHandler);
+};
+
+UsbEventHandler::UsbEventHandler(PlatformUsbContext context)
+ : running_(true), context_(context), thread_handle(0) {
+ base::PlatformThread::Create(0, this, &thread_handle);
+}
+
+UsbEventHandler::~UsbEventHandler() {}
+
+// Ref-counted wrapper for PlatformUsbContext.
+// It also manages the life-cycle of UsbEventHandler
+class UsbContext : public RefCountedThreadSafe<UsbContext> {
+ public:
+ UsbContext();
+ PlatformUsbContext context() const { return context_; }
+
private:
- bool running_;
- PlatformUsbContext context_;
+ friend class RefCountedThreadSafe<UsbContext>;
- DISALLOW_EVIL_CONSTRUCTORS(UsbEventHandler);
+ virtual ~UsbContext();
+ PlatformUsbContext context_;
+ scoped_ptr<UsbEventHandler> event_handler_;
};
-UsbService::UsbService() {
+UsbContext::UsbContext()
+ : context_(NULL) {
libusb_init(&context_);
- event_handler_ = new UsbEventHandler(context_);
+ event_handler_.reset(new UsbEventHandler(context_));
}
-UsbService::~UsbService() {}
-
-void UsbService::Cleanup() {
+UsbContext::~UsbContext() {
event_handler_->Stop();
- event_handler_ = NULL;
+ event_handler_.reset(NULL);
+ // The following statement will inform the event handler to stop waiting.
+ libusb_exit(context_);
+}
+
+// UsbDevice class uniquely represents a USB devices recognized by libusb and
+// maintains all its opened handles. It is assigned with an unique id by
+// UsbService. Once the device is disconnected it will invalidate all the
+// UsbDeviceHandle objects attached to it. The class is only visible to
+// UsbService and other classes need to access the device using its unique id.
+class UsbDevice : public base::NonThreadSafe {
+ public:
+ explicit UsbDevice(UsbContext* context, PlatformUsbDevice device,
+ const int unique_id, const uint16 vendor_id,
+ const uint16 product_id);
+ virtual ~UsbDevice();
+ PlatformUsbDevice device() const { return device_; }
+ int unique_id() const { return unique_id_; }
+ int vendor_id() const { return vendor_id_; }
+ int product_id() const { return product_id_; }
+
+ scoped_refptr<UsbDeviceHandle> OpenDevice(UsbService* service);
+ void CloseDeviceHandle(UsbDeviceHandle* device);
+
+ private:
+ // Retain the context so it will not be release before the destruction
+ // of the UsbDevice object.
+ scoped_refptr<UsbContext> context_;
+ vector<scoped_refptr<UsbDeviceHandle> > handles_;
+ const PlatformUsbDevice device_;
+ const uint16 unique_id_;
+ const uint16 vendor_id_;
+ const int product_id_;
+ DISALLOW_COPY_AND_ASSIGN(UsbDevice);
+};
+
+UsbDevice::UsbDevice(UsbContext* context, PlatformUsbDevice device,
+ const int unique_id, const uint16 vendor_id,
+ const uint16 product_id)
+ : context_(context),
+ device_(device),
+ unique_id_(unique_id),
+ vendor_id_(vendor_id),
+ product_id_(product_id) {
+ DCHECK(CalledOnValidThread());
+ libusb_ref_device(device_);
+}
+
+UsbDevice::~UsbDevice() {
+ DCHECK(CalledOnValidThread());
+ libusb_unref_device(device_);
+
+ // Device is lost.
+ // Invalidates all the opened handle.
+ for (vector<scoped_refptr<UsbDeviceHandle> >::iterator it = handles_.begin();
+ it != handles_.end(); ++it) {
+ it->get()->InternalClose();
+ }
+ STLClearObject(&handles_);
+}
+
+scoped_refptr<UsbDeviceHandle> UsbDevice::OpenDevice(UsbService* service) {
+ DCHECK(CalledOnValidThread());
+ PlatformUsbDeviceHandle handle;
+ if (0 == libusb_open(device_, &handle)) {
+ scoped_refptr<UsbDeviceHandle> wrapper =
+ make_scoped_refptr(new UsbDeviceHandle(service, unique_id_, vendor_id_,
+ product_id_, handle));
+ handles_.push_back(wrapper);
+ return wrapper;
+ }
+ return scoped_refptr<UsbDeviceHandle>();
+}
+
+void UsbDevice::CloseDeviceHandle(UsbDeviceHandle* device) {
+ DCHECK(CalledOnValidThread());
+ device->InternalClose();
+ for (vector<scoped_refptr<UsbDeviceHandle> >::iterator it = handles_.begin();
+ it != handles_.end(); ++it) {
+ if (it->get() == device) {
+ handles_.erase(it);
+ return;
+ }
+ }
+}
+
+UsbService::UsbService()
+ : context_(new UsbContext()),
+ next_unique_id_(1),
+ device_enumeration_scheduled_(false) {
+ // This class will be consequently called on FILE thread.
+ DetachFromThread();
}
-void UsbService::FindDevices(const uint16 vendor_id,
- const uint16 product_id,
- int interface_id,
- vector<scoped_refptr<UsbDevice> >* devices,
+UsbService::~UsbService() {
+ // The destructor will be called on UI thread.
+ DetachFromThread();
+}
+
+void UsbService::Shutdown() {
+ context_ = NULL;
+ for (DeviceMap::iterator it = devices_.begin(); it != devices_.end(); ++it) {
+ BrowserThread::DeleteSoon(BrowserThread::FILE, FROM_HERE, it->second);
+ }
+ devices_.clear();
+}
+
+void UsbService::FindDevices(const uint16 vendor_id, const uint16 product_id,
+ const int interface_id, vector<int>* devices,
const base::Callback<void()>& callback) {
- DCHECK(event_handler_) << "FindDevices called after event handler stopped.";
+ DCHECK(CalledOnValidThread());
#if defined(OS_CHROMEOS)
// ChromeOS builds on non-ChromeOS machines (dev) should not attempt to
// use permission broker.
@@ -105,10 +245,10 @@ void UsbService::FindDevices(const uint16 vendor_id,
}
void UsbService::FindDevicesImpl(const uint16 vendor_id,
- const uint16 product_id,
- vector<scoped_refptr<UsbDevice> >* devices,
+ const uint16 product_id, vector<int>* devices,
const base::Callback<void()>& callback,
bool success) {
+ DCHECK(CalledOnValidThread());
base::ScopedClosureRunner run_callback(callback);
devices->clear();
@@ -116,91 +256,92 @@ void UsbService::FindDevicesImpl(const uint16 vendor_id,
// If the permission broker was unable to obtain permission for the specified
// devices then there is no point in attempting to enumerate the devices. On
// platforms without a permission broker, we assume permission is granted.
- if (!success)
- return;
+ if (!success) return;
- DeviceVector enumerated_devices;
- EnumerateDevices(&enumerated_devices);
- if (enumerated_devices.empty())
- return;
+ EnumerateDevices();
- for (unsigned int i = 0; i < enumerated_devices.size(); ++i) {
- PlatformUsbDevice device = enumerated_devices[i].device();
- if (DeviceMatches(device, vendor_id, product_id)) {
- UsbDevice* const wrapper = LookupOrCreateDevice(device);
- if (wrapper)
- devices->push_back(wrapper);
- }
+ for (DeviceMap::iterator it = devices_.begin(); it != devices_.end(); ++it) {
+ if (DeviceMatches(it->second, vendor_id, product_id))
+ devices->push_back(it->second->unique_id());
}
}
-void UsbService::CloseDevice(scoped_refptr<UsbDevice> device) {
- DCHECK(event_handler_) << "CloseDevice called after event handler stopped.";
-
- PlatformUsbDevice platform_device = libusb_get_device(device->handle());
- if (!ContainsKey(devices_, platform_device)) {
- LOG(WARNING) << "CloseDevice called for device we're not tracking!";
- return;
+scoped_refptr<UsbDeviceHandle> UsbService::OpenDevice(int device) {
+ DCHECK(CalledOnValidThread());
+ EnumerateDevices();
+ for (DeviceMap::iterator it = devices_.begin(); it != devices_.end(); ++it) {
+ if (it->second->unique_id() == device)
+ return it->second->OpenDevice(this);
}
-
- devices_.erase(platform_device);
- libusb_close(device->handle());
-}
-
-UsbService::RefCountedPlatformUsbDevice::RefCountedPlatformUsbDevice(
- PlatformUsbDevice device) : device_(device) {
- libusb_ref_device(device_);
+ return NULL;
}
-UsbService::RefCountedPlatformUsbDevice::RefCountedPlatformUsbDevice(
- const RefCountedPlatformUsbDevice& other) : device_(other.device_) {
- libusb_ref_device(device_);
-}
+void UsbService::CloseDeviceHandle(scoped_refptr<UsbDeviceHandle> device) {
+ DCHECK(CalledOnValidThread());
+ int id = device->device();
-UsbService::RefCountedPlatformUsbDevice::~RefCountedPlatformUsbDevice() {
- libusb_unref_device(device_);
+ for (DeviceMap::iterator it = devices_.begin(); it != devices_.end(); ++it) {
+ if (it->second->unique_id() == id) {
+ it->second->CloseDeviceHandle(device);
+ break;
+ }
+ }
}
-PlatformUsbDevice UsbService::RefCountedPlatformUsbDevice::device() {
- return device_;
+void UsbService::ScheduleEnumerateDevice() {
+ DCHECK(CalledOnValidThread());
+ if (device_enumeration_scheduled_)
+ return;
+ device_enumeration_scheduled_ = true;
+ BrowserThread::PostTask(
+ BrowserThread::FILE, FROM_HERE,
+ base::Bind(&UsbService::EnumerateDevices, base::Unretained(this)));
}
-void UsbService::EnumerateDevices(DeviceVector* output) {
- STLClearObject(output);
-
+void UsbService::EnumerateDevices() {
+ DCHECK(CalledOnValidThread());
+ device_enumeration_scheduled_ = false;
libusb_device** devices = NULL;
- const ssize_t device_count = libusb_get_device_list(context_, &devices);
+ const ssize_t device_count =
+ libusb_get_device_list(context_->context(), &devices);
if (device_count < 0)
return;
- for (int i = 0; i < device_count; ++i) {
- libusb_device* device = devices[i];
- libusb_ref_device(device);
- output->push_back(RefCountedPlatformUsbDevice(device));
+ set<int> connected_devices;
+ vector<PlatformUsbDevice> disconnected_devices;
+
+ // Populates new devices.
+ for (ssize_t i = 0; i < device_count; ++i) {
+ if (!ContainsKey(devices_, devices[i])) {
+ libusb_device_descriptor descriptor;
+ if (0 != libusb_get_device_descriptor(devices[i], &descriptor))
+ continue;
+ devices_[devices[i]] = new UsbDevice(context_.get(), devices[i],
+ next_unique_id_, descriptor.idVendor,
+ descriptor.idProduct);
+ ++next_unique_id_;
+ }
+ connected_devices.insert(devices_[devices[i]]->unique_id());
+ }
+
+ // Find disconnected devices.
+ for (DeviceMap::iterator it = devices_.begin(); it != devices_.end(); ++it) {
+ if (!ContainsKey(connected_devices, it->second->unique_id())) {
+ disconnected_devices.push_back(it->first);
+ }
}
+ // Remove disconnected devices from devices_.
+ for (size_t i = 0; i < disconnected_devices.size(); ++i) {
+ // This should delete those devices and invalidate their handles.
+ // It might take long.
+ delete devices_[disconnected_devices[i]];
+ devices_.erase(disconnected_devices[i]);
+ }
libusb_free_device_list(devices, true);
}
-bool UsbService::DeviceMatches(PlatformUsbDevice device,
- const uint16 vendor_id,
+bool UsbService::DeviceMatches(const UsbDevice* device, const uint16 vendor_id,
const uint16 product_id) {
- libusb_device_descriptor descriptor;
- if (libusb_get_device_descriptor(device, &descriptor))
- return false;
- return descriptor.idVendor == vendor_id && descriptor.idProduct == product_id;
-}
-
-UsbDevice* UsbService::LookupOrCreateDevice(PlatformUsbDevice device) {
- if (!ContainsKey(devices_, device)) {
- libusb_device_handle* handle = NULL;
- if (libusb_open(device, &handle)) {
- LOG(WARNING) << "Could not open device.";
- return NULL;
- }
-
- UsbDevice* wrapper = new UsbDevice(this, handle);
- devices_[device] = wrapper;
- }
- return devices_[device].get();
+ return device->vendor_id() == vendor_id && device->product_id() == product_id;
}
« no previous file with comments | « chrome/browser/usb/usb_service.h ('k') | chrome/browser/usb/usb_service_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698