From 4bce1481c5cfbd402973c2a488b36499ed44af2e Mon Sep 17 00:00:00 2001 From: Timur Pocheptsov Date: Wed, 11 Oct 2017 10:10:27 +0200 Subject: IOBluetooth: warn about incorrent thread/runloop IOBluetooth is heavily based on CFRunLoops. An attempt to use it on a thread, that does not properly run CFRunLoop results in callbacks never firing and thus QBluetooth classes never finishing their jobs, including: - device discovery - service discovery - RFCOMM/LCAP2 - Bluetooth server - Bluetooth socket etc. While we cannot fix the core problem until we have a properly working CoreFoundation event dispatcher, we can at least issue a warning so that people do not waste their time debugging this well-known limitation. Task-number: QTBUG-63630 Change-Id: Iefa4d675ea0962167bdfede640d2087dbdf37b18 Reviewed-by: Alex Blasche --- src/bluetooth/osx/osxbtutility.mm | 15 ++++++++++++++- src/bluetooth/osx/osxbtutility_p.h | 3 ++- src/bluetooth/qbluetoothdevicediscoveryagent_osx.mm | 2 ++ src/bluetooth/qbluetoothlocaldevice_osx.mm | 2 ++ src/bluetooth/qbluetoothserver_osx.mm | 2 ++ src/bluetooth/qbluetoothservicediscoveryagent_osx.mm | 2 ++ src/bluetooth/qbluetoothsocket_osx.mm | 8 ++++++++ 7 files changed, 32 insertions(+), 2 deletions(-) diff --git a/src/bluetooth/osx/osxbtutility.mm b/src/bluetooth/osx/osxbtutility.mm index 1508c89f..e17006de 100644 --- a/src/bluetooth/osx/osxbtutility.mm +++ b/src/bluetooth/osx/osxbtutility.mm @@ -48,6 +48,7 @@ #ifndef QT_IOS_BLUETOOTH #import +#import #if QT_MAC_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_10_12, __IPHONE_NA) #import #endif @@ -164,7 +165,19 @@ QString qt_error_string(IOReturn errorCode) } } -#endif +void qt_test_iobluetooth_runloop() +{ + // IOBluetooth heavily relies on a CFRunLoop machinery in a way it dispatches + // its callbacks. Technically, having a QThread with CFRunLoop-based event + // dispatcher would suffice. At the moment of writing we do not have such + // event dispatcher, so we only can work on the main thread. + if (CFRunLoopGetMain() != CFRunLoopGetCurrent()) { + qCWarning(QT_BT_OSX) << "IOBluetooth works only on the main thread or a" + << "thread with a running CFRunLoop"; + } +} + +#endif // !QT_IOS_BLUETOOTH // Apple has: CBUUID, NSUUID, CFUUID, IOBluetoothSDPUUID diff --git a/src/bluetooth/osx/osxbtutility_p.h b/src/bluetooth/osx/osxbtutility_p.h index 148ebc0b..5ded5e7f 100644 --- a/src/bluetooth/osx/osxbtutility_p.h +++ b/src/bluetooth/osx/osxbtutility_p.h @@ -287,8 +287,9 @@ BluetoothDeviceAddress iobluetooth_address(const QBluetoothAddress &address); ObjCStrongReference iobluetooth_uuid(const QBluetoothUuid &uuid); QBluetoothUuid qt_uuid(IOBluetoothSDPUUID *uuid); QString qt_error_string(IOReturn errorCode); +void qt_test_iobluetooth_runloop(); -#endif +#endif // !QT_IOS_BLUETOOTH QBluetoothUuid qt_uuid(CBUUID *uuid); CFStrongReference cf_uuid(const QBluetoothUuid &qtUuid); diff --git a/src/bluetooth/qbluetoothdevicediscoveryagent_osx.mm b/src/bluetooth/qbluetoothdevicediscoveryagent_osx.mm index 9e3f6a57..7f037596 100644 --- a/src/bluetooth/qbluetoothdevicediscoveryagent_osx.mm +++ b/src/bluetooth/qbluetoothdevicediscoveryagent_osx.mm @@ -245,6 +245,8 @@ void QBluetoothDeviceDiscoveryAgentPrivate::startClassic() Q_ASSERT(requestedMethods & QBluetoothDeviceDiscoveryAgent::ClassicMethod); Q_ASSERT(agentState == NonActive); + OSXBluetooth::qt_test_iobluetooth_runloop(); + if (!inquiry) { // The first Classic scan for this DDA. inquiry.reset([[DeviceInquiryObjC alloc]initWithDelegate:this]); diff --git a/src/bluetooth/qbluetoothlocaldevice_osx.mm b/src/bluetooth/qbluetoothlocaldevice_osx.mm index 45fa310a..cb5625d6 100644 --- a/src/bluetooth/qbluetoothlocaldevice_osx.mm +++ b/src/bluetooth/qbluetoothlocaldevice_osx.mm @@ -464,6 +464,8 @@ void QBluetoothLocalDevice::requestPairing(const QBluetoothAddress &address, Pai return; } + OSXBluetooth::qt_test_iobluetooth_runloop(); + return d_ptr->requestPairing(address, pairing); } diff --git a/src/bluetooth/qbluetoothserver_osx.mm b/src/bluetooth/qbluetoothserver_osx.mm index 8896651d..8d7eeb29 100644 --- a/src/bluetooth/qbluetoothserver_osx.mm +++ b/src/bluetooth/qbluetoothserver_osx.mm @@ -291,6 +291,8 @@ bool QBluetoothServer::listen(const QBluetoothAddress &address, quint16 port) { typedef QBluetoothServerPrivate::ObjCListener ObjCListener; + OSXBluetooth::qt_test_iobluetooth_runloop(); + if (d_ptr->listener) { qCWarning(QT_BT_OSX) << "already in listen mode, close server first"; return false; diff --git a/src/bluetooth/qbluetoothservicediscoveryagent_osx.mm b/src/bluetooth/qbluetoothservicediscoveryagent_osx.mm index 25bb2447..0b2f593d 100644 --- a/src/bluetooth/qbluetoothservicediscoveryagent_osx.mm +++ b/src/bluetooth/qbluetoothservicediscoveryagent_osx.mm @@ -526,6 +526,8 @@ QBluetoothAddress QBluetoothServiceDiscoveryAgent::remoteAddress() const void QBluetoothServiceDiscoveryAgent::start(DiscoveryMode mode) { + OSXBluetooth::qt_test_iobluetooth_runloop(); + if (d_ptr->discoveryState() == QBluetoothServiceDiscoveryAgentPrivate::Inactive && d_ptr->error != InvalidBluetoothAdapterError) { diff --git a/src/bluetooth/qbluetoothsocket_osx.mm b/src/bluetooth/qbluetoothsocket_osx.mm index 75712868..59fb66f2 100644 --- a/src/bluetooth/qbluetoothsocket_osx.mm +++ b/src/bluetooth/qbluetoothsocket_osx.mm @@ -443,6 +443,8 @@ qint64 QBluetoothSocket::bytesToWrite() const void QBluetoothSocket::connectToService(const QBluetoothServiceInfo &service, OpenMode openMode) { + OSXBluetooth::qt_test_iobluetooth_runloop(); + // Report this problem early, potentially avoid device discovery: if (socketType() == QBluetoothServiceInfo::UnknownProtocol) { qCWarning(QT_BT_OSX) << Q_FUNC_INFO << "cannot connect with 'UnknownProtocol' type"; @@ -480,6 +482,8 @@ void QBluetoothSocket::connectToService(const QBluetoothServiceInfo &service, Op void QBluetoothSocket::connectToService(const QBluetoothAddress &address, const QBluetoothUuid &uuid, OpenMode openMode) { + OSXBluetooth::qt_test_iobluetooth_runloop(); + // Report this problem early, avoid device discovery: if (socketType() == QBluetoothServiceInfo::UnknownProtocol) { qCWarning(QT_BT_OSX) << Q_FUNC_INFO << "cannot connect with 'UnknownProtocol' type"; @@ -505,6 +509,8 @@ void QBluetoothSocket::connectToService(const QBluetoothAddress &address, const void QBluetoothSocket::connectToService(const QBluetoothAddress &address, quint16 port, OpenMode openMode) { + OSXBluetooth::qt_test_iobluetooth_runloop(); + if (socketType() == QBluetoothServiceInfo::UnknownProtocol) { qCWarning(QT_BT_OSX) << Q_FUNC_INFO << "cannot connect with 'UnknownProtocol' type"; d_ptr->errorString = QCoreApplication::translate(SOCKET, SOC_NETWORK_ERROR); @@ -571,6 +577,8 @@ void QBluetoothSocket::setSocketError(QBluetoothSocket::SocketError socketError) void QBluetoothSocket::doDeviceDiscovery(const QBluetoothServiceInfo &service, OpenMode openMode) { + OSXBluetooth::qt_test_iobluetooth_runloop(); + setSocketState(ServiceLookupState); if (d_ptr->discoveryAgent) -- cgit v1.2.3