adb: fix deadlock between transport_unref and usb_close.
Fix a deadlock that happened when a reader/writer thread released a
transport while the hotplug thread attempted to handle a device
disconnection. Decrementing a transport refcount to zero would hold the
global transport mutex and attempt to take the usb handles mutex, while
the hotplug thread would hold the usb handles mutex and try to call
unregister_usb_transport, which would attempt to take the global
transport mutex.
Resolve this by making transport_unref not take the global transport
mutex.
Bug: http://b/62423753
Test: python test_device.py
Change-Id: Ib48b80a2091a254527f3a7d945b6a11fae61f937
diff --git a/adb/transport.cpp b/adb/transport.cpp
index 308ee8d..2bbbefd 100644
--- a/adb/transport.cpp
+++ b/adb/transport.cpp
@@ -603,15 +603,15 @@
static void transport_unref(atransport* t) {
CHECK(t != nullptr);
- std::lock_guard<std::mutex> lock(transport_lock);
- CHECK_GT(t->ref_count, 0u);
- t->ref_count--;
- if (t->ref_count == 0) {
+ size_t old_refcount = t->ref_count--;
+ CHECK_GT(old_refcount, 0u);
+
+ if (old_refcount == 1u) {
D("transport: %s unref (kicking and closing)", t->serial);
t->close(t);
remove_transport(t);
} else {
- D("transport: %s unref (count=%zu)", t->serial, t->ref_count);
+ D("transport: %s unref (count=%zu)", t->serial, old_refcount - 1);
}
}
diff --git a/adb/transport.h b/adb/transport.h
index 57fc988..4a89ed9 100644
--- a/adb/transport.h
+++ b/adb/transport.h
@@ -61,7 +61,7 @@
// class in one go is a very large change. Given how bad our testing is,
// it's better to do this piece by piece.
- atransport(ConnectionState state = kCsOffline) : connection_state_(state) {
+ atransport(ConnectionState state = kCsOffline) : ref_count(0), connection_state_(state) {
transport_fde = {};
protocol_version = A_VERSION;
max_payload = MAX_PAYLOAD;
@@ -88,7 +88,7 @@
int fd = -1;
int transport_socket = -1;
fdevent transport_fde;
- size_t ref_count = 0;
+ std::atomic<size_t> ref_count;
uint32_t sync_token = 0;
bool online = false;
TransportType type = kTransportAny;