diff options
-rw-r--r-- | .cmake.conf | 2 | ||||
-rw-r--r-- | .qmake.conf | 2 | ||||
-rw-r--r-- | coin/module_config.yaml | 1 | ||||
-rw-r--r-- | dependencies.yaml | 4 | ||||
-rw-r--r-- | src/serialport/CMakeLists.txt | 2 | ||||
-rw-r--r-- | src/serialport/doc/src/index.qdoc | 28 | ||||
-rw-r--r-- | src/serialport/qserialport.cpp | 51 | ||||
-rw-r--r-- | src/serialport/qserialport_p.h | 5 | ||||
-rw-r--r-- | src/serialport/qserialport_unix.cpp | 4 | ||||
-rw-r--r-- | src/serialport/qserialportinfo_unix.cpp | 53 | ||||
-rw-r--r-- | src/serialport/qwinoverlappedionotifier.cpp | 38 | ||||
-rw-r--r-- | tests/auto/qserialport/tst_qserialport.cpp | 40 |
12 files changed, 127 insertions, 103 deletions
diff --git a/.cmake.conf b/.cmake.conf index ac3b6f4a..e62fae5e 100644 --- a/.cmake.conf +++ b/.cmake.conf @@ -1,2 +1,2 @@ -set(QT_REPO_MODULE_VERSION "6.2.4") +set(QT_REPO_MODULE_VERSION "6.2.8") set(QT_REPO_MODULE_PRERELEASE_VERSION_SEGMENT "") diff --git a/.qmake.conf b/.qmake.conf index 5f8f68f6..a0cf48b9 100644 --- a/.qmake.conf +++ b/.qmake.conf @@ -2,4 +2,4 @@ load(qt_build_config) DEFINES += QT_NO_FOREACH QT_NO_JAVA_STYLE_ITERATORS -MODULE_VERSION = 6.2.4 +MODULE_VERSION = 6.2.8 diff --git a/coin/module_config.yaml b/coin/module_config.yaml index 16d158c6..64cbbaae 100644 --- a/coin/module_config.yaml +++ b/coin/module_config.yaml @@ -1,4 +1,5 @@ version: 2 +alias: qtserialport accept_configuration: condition: property property: features diff --git a/dependencies.yaml b/dependencies.yaml index a49b554c..1b97928a 100644 --- a/dependencies.yaml +++ b/dependencies.yaml @@ -1,4 +1,4 @@ dependencies: - ../qtbase: - ref: 7a5b98e0fefcbf061cb9e3d9edecc75fff364519 + ../tqtc-qtbase: + ref: 4c1c38dede55565afa846685b3e19cf8f1cfed0c required: true diff --git a/src/serialport/CMakeLists.txt b/src/serialport/CMakeLists.txt index 35810e0b..7ad0073e 100644 --- a/src/serialport/CMakeLists.txt +++ b/src/serialport/CMakeLists.txt @@ -1,7 +1,7 @@ ##################################################################### ## SerialPort Module: ##################################################################### -qt_find_package(Libudev) +qt_find_package(Libudev PROVIDED_TARGETS PkgConfig::Libudev MODULE_NAME global QMAKE_LIB libudev) qt_internal_add_module(SerialPort SOURCES diff --git a/src/serialport/doc/src/index.qdoc b/src/serialport/doc/src/index.qdoc index 2c349e44..54e25c90 100644 --- a/src/serialport/doc/src/index.qdoc +++ b/src/serialport/doc/src/index.qdoc @@ -31,9 +31,9 @@ \title Qt Serial Port \brief Provides an API to make serial programming simple and portable. - Qt Serial Port provides the basic functionality, which includes - configuring, I/O operations, getting and setting the control signals of the - RS-232 pinouts. + Qt Serial Port provides basic functionality for configuration, I/O + operations, and getting and setting the control signals of the RS-232 + pinouts. The following items are not supported by this module: \list @@ -43,27 +43,17 @@ \li Pinout signal change notification. \endlist - To use the serial port in your application, add the following include - statement: + \section1 Using the Module - \code - #include <QSerialPort> - \endcode + \include {module-use.qdocinc} {using the c++ api} - For information about available serial ports, use the following include - statement: + \section2 Building with Cmake - \code - #include <QSerialPortInfo> - \endcode + \include {module-use.qdocinc} {building with cmake} {SerialPort} - To use the module with cmake, use the \c{find_package()} command to locate - the needed module components in the \c{Qt6} package: - \include qtserialport-module-use.qdocinc cmakebuild + \section2 Building with qmake - To use the module for building with qmake, add the module as a value of the - \c QT variable in the project's .pro file: - \include qtserialport-module-use.qdocinc qmakebuild + \include {module-use.qdocinc} {building_with_qmake} {serialport} \section1 Module Evolution \l{Changes to Qt SerialPort} lists important changes in the module API diff --git a/src/serialport/qserialport.cpp b/src/serialport/qserialport.cpp index bc9504c2..c7d7e76d 100644 --- a/src/serialport/qserialport.cpp +++ b/src/serialport/qserialport.cpp @@ -101,6 +101,7 @@ void QSerialPortPrivate::setError(const QSerialPortErrorInfo &errorInfo) q->setErrorString(errorInfo.errorString); error.setValue(errorInfo.errorCode); + error.notify(); emit q->errorOccurred(error); } @@ -606,16 +607,16 @@ qint32 QSerialPort::baudRate(Directions directions) const bool QSerialPort::setDataBits(DataBits dataBits) { Q_D(QSerialPort); - + d->dataBits.removeBindingUnlessInWrapper(); const auto currentDataBits = d->dataBits.value(); if (!isOpen() || d->setDataBits(dataBits)) { - d->dataBits.setValue(dataBits); - if (currentDataBits != dataBits) + d->dataBits.setValueBypassingBindings(dataBits); + if (currentDataBits != dataBits) { + d->dataBits.notify(); emit dataBitsChanged(dataBits); + } return true; } - d->dataBits.setValue(currentDataBits); // removes the binding if necessary, keep - return false; } @@ -657,16 +658,16 @@ QBindable<QSerialPort::DataBits> QSerialPort::bindableDataBits() bool QSerialPort::setParity(Parity parity) { Q_D(QSerialPort); - + d->parity.removeBindingUnlessInWrapper(); const auto currentParity = d->parity.value(); if (!isOpen() || d->setParity(parity)) { - d->parity.setValue(parity); - if (currentParity != parity) + d->parity.setValueBypassingBindings(parity); + if (currentParity != parity) { + d->parity.notify(); emit parityChanged(parity); + } return true; } - d->parity.setValue(currentParity); // removes the binding if necessary, keep - return false; } @@ -707,16 +708,16 @@ QBindable<QSerialPort::Parity> QSerialPort::bindableParity() bool QSerialPort::setStopBits(StopBits stopBits) { Q_D(QSerialPort); - + d->stopBits.removeBindingUnlessInWrapper(); const auto currentStopBits = d->stopBits.value(); if (!isOpen() || d->setStopBits(stopBits)) { - d->stopBits.setValue(stopBits); - if (currentStopBits != stopBits) + d->stopBits.setValueBypassingBindings(stopBits); + if (currentStopBits != stopBits) { + d->stopBits.notify(); emit stopBitsChanged(stopBits); + } return true; } - d->stopBits.setValue(currentStopBits); // removes the binding if necessary, keep - return false; } @@ -757,16 +758,16 @@ QBindable<bool> QSerialPort::bindableStopBits() bool QSerialPort::setFlowControl(FlowControl flowControl) { Q_D(QSerialPort); - + d->flowControl.removeBindingUnlessInWrapper(); const auto currentFlowControl = d->flowControl.value(); if (!isOpen() || d->setFlowControl(flowControl)) { - d->flowControl.setValue(flowControl); - if (currentFlowControl != flowControl) + d->flowControl.setValueBypassingBindings(flowControl); + if (currentFlowControl != flowControl) { + d->flowControl.notify(); emit flowControlChanged(flowControl); + } return true; } - d->flowControl.setValue(currentFlowControl); // removes the binding if necessary, keep - return false; } @@ -1177,21 +1178,21 @@ bool QSerialPort::waitForBytesWritten(int msecs) bool QSerialPort::setBreakEnabled(bool set) { Q_D(QSerialPort); - + d->isBreakEnabled.removeBindingUnlessInWrapper(); const auto currentSet = d->isBreakEnabled.value(); if (isOpen()) { if (d->setBreakEnabled(set)) { - d->isBreakEnabled.setValue(set); - if (currentSet != set) + d->isBreakEnabled.setValueBypassingBindings(set); + if (currentSet != set) { + d->isBreakEnabled.notify(); emit breakEnabledChanged(set); + } return true; } } else { d->setError(QSerialPortErrorInfo(QSerialPort::NotOpenError)); qWarning("%s: device not open", Q_FUNC_INFO); } - d->isBreakEnabled.setValue(currentSet); // removes the binding if necessary, keep - return false; } diff --git a/src/serialport/qserialport_p.h b/src/serialport/qserialport_p.h index e26531a4..c5347258 100644 --- a/src/serialport/qserialport_p.h +++ b/src/serialport/qserialport_p.h @@ -60,11 +60,12 @@ #include <private/qiodevice_p.h> #include <private/qproperty_p.h> +#include <memory> + #if defined(Q_OS_WIN32) # include <qt_windows.h> #elif defined(Q_OS_UNIX) # include <QtCore/qlockfile.h> -# include <QtCore/qscopedpointer.h> # include <QtCore/qfileinfo.h> # include <QtCore/qstringlist.h> # include <limits.h> @@ -291,7 +292,7 @@ public: qint64 pendingBytesWritten = 0; bool writeSequenceStarted = false; - QScopedPointer<QLockFile> lockFileScopedPointer; + std::unique_ptr<QLockFile> lockFileScopedPointer; #endif }; diff --git a/src/serialport/qserialport_unix.cpp b/src/serialport/qserialport_unix.cpp index 294782b5..7605fcb4 100644 --- a/src/serialport/qserialport_unix.cpp +++ b/src/serialport/qserialport_unix.cpp @@ -316,7 +316,7 @@ bool QSerialPortPrivate::open(QIODevice::OpenMode mode) return false; } - QScopedPointer<QLockFile> newLockFileScopedPointer(new QLockFile(lockFilePath)); + auto newLockFileScopedPointer = std::make_unique<QLockFile>(lockFilePath); if (!newLockFileScopedPointer->tryLock()) { setError(QSerialPortErrorInfo(QSerialPort::PermissionError, QSerialPort::tr("Permission error while locking the device"))); @@ -349,7 +349,7 @@ bool QSerialPortPrivate::open(QIODevice::OpenMode mode) return false; } - lockFileScopedPointer.swap(newLockFileScopedPointer); + lockFileScopedPointer = std::move(newLockFileScopedPointer); return true; } diff --git a/src/serialport/qserialportinfo_unix.cpp b/src/serialport/qserialportinfo_unix.cpp index 93760e22..7239cdb9 100644 --- a/src/serialport/qserialportinfo_unix.cpp +++ b/src/serialport/qserialportinfo_unix.cpp @@ -46,10 +46,11 @@ #include <QtCore/qlockfile.h> #include <QtCore/qfile.h> #include <QtCore/qdir.h> -#include <QtCore/qscopedpointer.h> #include <private/qcore_unix_p.h> +#include <memory> + #include <errno.h> #include <sys/types.h> // kill #include <signal.h> // kill @@ -313,29 +314,22 @@ QList<QSerialPortInfo> availablePortsBySysfs(bool &ok) return serialPortInfoList; } -struct ScopedPointerUdevDeleter -{ - static inline void cleanup(struct ::udev *pointer) +struct udev_deleter { + void operator()(struct ::udev *pointer) const { ::udev_unref(pointer); } -}; - -struct ScopedPointerUdevEnumeratorDeleter -{ - static inline void cleanup(struct ::udev_enumerate *pointer) + void operator()(struct ::udev_enumerate *pointer) const { ::udev_enumerate_unref(pointer); } -}; - -struct ScopedPointerUdevDeviceDeleter -{ - static inline void cleanup(struct ::udev_device *pointer) + void operator()(struct ::udev_device *pointer) const { ::udev_device_unref(pointer); } }; +template <typename T> +using udev_ptr = std::unique_ptr<T, udev_deleter>; #ifndef LINK_LIBUDEV Q_GLOBAL_STATIC(QLibrary, udevLibrary) @@ -396,21 +390,20 @@ QList<QSerialPortInfo> availablePortsByUdev(bool &ok) return QList<QSerialPortInfo>(); #endif - QScopedPointer<struct ::udev, ScopedPointerUdevDeleter> udev(::udev_new()); + const udev_ptr<struct ::udev> udev(::udev_new()); if (!udev) return QList<QSerialPortInfo>(); - QScopedPointer<udev_enumerate, ScopedPointerUdevEnumeratorDeleter> - enumerate(::udev_enumerate_new(udev.data())); + const udev_ptr<udev_enumerate> enumerate(::udev_enumerate_new(udev.get())); if (!enumerate) return QList<QSerialPortInfo>(); - ::udev_enumerate_add_match_subsystem(enumerate.data(), "tty"); - ::udev_enumerate_scan_devices(enumerate.data()); + ::udev_enumerate_add_match_subsystem(enumerate.get(), "tty"); + ::udev_enumerate_scan_devices(enumerate.get()); - udev_list_entry *devices = ::udev_enumerate_get_list_entry(enumerate.data()); + udev_list_entry *devices = ::udev_enumerate_get_list_entry(enumerate.get()); QList<QSerialPortInfo> serialPortInfoList; udev_list_entry *dev_list_entry; @@ -418,29 +411,29 @@ QList<QSerialPortInfo> availablePortsByUdev(bool &ok) ok = true; - QScopedPointer<udev_device, ScopedPointerUdevDeviceDeleter> + const udev_ptr<udev_device> dev(::udev_device_new_from_syspath( - udev.data(), ::udev_list_entry_get_name(dev_list_entry))); + udev.get(), ::udev_list_entry_get_name(dev_list_entry))); if (!dev) return serialPortInfoList; QSerialPortInfoPrivate priv; - priv.device = deviceLocation(dev.data()); - priv.portName = deviceName(dev.data()); + priv.device = deviceLocation(dev.get()); + priv.portName = deviceName(dev.get()); - udev_device *parentdev = ::udev_device_get_parent(dev.data()); + udev_device *parentdev = ::udev_device_get_parent(dev.get()); if (parentdev) { const QString driverName = deviceDriver(parentdev); if (isSerial8250Driver(driverName) && !isValidSerial8250(priv.device)) continue; - priv.description = deviceDescription(dev.data()); - priv.manufacturer = deviceManufacturer(dev.data()); - priv.serialNumber = deviceSerialNumber(dev.data()); - priv.vendorIdentifier = deviceVendorIdentifier(dev.data(), priv.hasVendorIdentifier); - priv.productIdentifier = deviceProductIdentifier(dev.data(), priv.hasProductIdentifier); + priv.description = deviceDescription(dev.get()); + priv.manufacturer = deviceManufacturer(dev.get()); + priv.serialNumber = deviceSerialNumber(dev.get()); + priv.vendorIdentifier = deviceVendorIdentifier(dev.get(), priv.hasVendorIdentifier); + priv.productIdentifier = deviceProductIdentifier(dev.get(), priv.hasProductIdentifier); } else { if (!isRfcommDevice(priv.portName) && !isVirtualNullModemDevice(priv.portName) diff --git a/src/serialport/qwinoverlappedionotifier.cpp b/src/serialport/qwinoverlappedionotifier.cpp index c4d842dd..dd3490d3 100644 --- a/src/serialport/qwinoverlappedionotifier.cpp +++ b/src/serialport/qwinoverlappedionotifier.cpp @@ -129,6 +129,7 @@ public: HANDLE hSemaphore = nullptr; HANDLE hResultsMutex = nullptr; QAtomicInt waiting; + QAtomicInt signalSent; QQueue<IOResult> results; }; @@ -395,16 +396,45 @@ void QWinOverlappedIoNotifierPrivate::notify(DWORD numberOfBytes, DWORD errorCod Q_Q(QWinOverlappedIoNotifier); WaitForSingleObject(hResultsMutex, INFINITE); results.enqueue(IOResult(numberOfBytes, errorCode, overlapped)); - ReleaseMutex(hResultsMutex); ReleaseSemaphore(hSemaphore, 1, NULL); - if (!waiting) + ReleaseMutex(hResultsMutex); + // Do not send a signal if we didn't process the previous one. + // This is done to prevent soft memory leaks when working in a completely + // synchronous way. + if (!waiting && !signalSent.loadAcquire()) { + signalSent.storeRelease(1); emit q->_q_notify(); + } } void QWinOverlappedIoNotifierPrivate::_q_notified() { - if (WaitForSingleObject(hSemaphore, 0) == WAIT_OBJECT_0) - dispatchNextIoResult(); + Q_Q(QWinOverlappedIoNotifier); + signalSent.storeRelease(0); // signal processed - ready for a new one + if (WaitForSingleObject(hSemaphore, 0) == WAIT_OBJECT_0) { + // As we do not queue signals anymore, we need to process the whole + // queue at once. + WaitForSingleObject(hResultsMutex, INFINITE); + QQueue<IOResult> values; + results.swap(values); + // Decreasing the semaphore count to keep it in sync with the number + // of messages in queue. As ReleaseSemaphore does not accept negative + // values, this is sort of a recommended way to go: + // https://learn.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-releasesemaphore#remarks + int numToDecrease = values.size() - 1; + while (numToDecrease > 0) { + WaitForSingleObject(hSemaphore, 0); + --numToDecrease; + } + ReleaseMutex(hResultsMutex); + // 'q' can go out of scope if the user decides to close the serial port + // while processing some answer. So we need to guard against that. + QPointer<QWinOverlappedIoNotifier> qptr(q); + while (!values.empty() && qptr) { + IOResult ioresult = values.dequeue(); + emit qptr->notified(ioresult.numberOfBytes, ioresult.errorCode, ioresult.overlapped); + } + } } OVERLAPPED *QWinOverlappedIoNotifierPrivate::dispatchNextIoResult() diff --git a/tests/auto/qserialport/tst_qserialport.cpp b/tests/auto/qserialport/tst_qserialport.cpp index 268b8475..f700a178 100644 --- a/tests/auto/qserialport/tst_qserialport.cpp +++ b/tests/auto/qserialport/tst_qserialport.cpp @@ -1404,30 +1404,38 @@ void tst_QSerialPort::bindingsAndProperties() QCOMPARE(errorChangedSpy.at(0).value(0).toInt(), QSerialPort::SerialPortError::NoError); sp.setPortName(m_receiverPortName); - sp.open(QIODevice::ReadOnly); + const bool portOpened = sp.open(QIODevice::ReadOnly); // -- break enabled - QProperty<bool> beProp(false); - QCOMPARE(beProp.value(), false); + if (portOpened) { + QProperty<bool> beProp(false); + QCOMPARE(beProp.value(), false); - sp.bindableIsBreakEnabled().setBinding(Qt::makePropertyBinding(beProp)); - QCOMPARE(sp.isBreakEnabled(), false); + sp.bindableIsBreakEnabled().setBinding(Qt::makePropertyBinding(beProp)); + QCOMPARE(sp.isBreakEnabled(), false); - const QSignalSpy breakEnabledChangedSpy(&sp, &QSerialPort::breakEnabledChanged); + const QSignalSpy breakEnabledChangedSpy(&sp, &QSerialPort::breakEnabledChanged); - beProp = true; - QCOMPARE(sp.isBreakEnabled(), true); - QCOMPARE(breakEnabledChangedSpy.count(), 1); - QCOMPARE(breakEnabledChangedSpy.at(0).value(0).toBool(), true); + beProp = true; + QCOMPARE(sp.isBreakEnabled(), true); + QCOMPARE(breakEnabledChangedSpy.size(), 1); + QCOMPARE(breakEnabledChangedSpy.at(0).value(0).toBool(), true); - beProp.setBinding(sp.bindableIsBreakEnabled().makeBinding()); - sp.setBreakEnabled(false); - QCOMPARE(beProp.value(), false); + beProp.setBinding(sp.bindableIsBreakEnabled().makeBinding()); + sp.setBreakEnabled(false); + QCOMPARE(beProp.value(), false); - beProp.setBinding([&] { return sp.isBreakEnabled(); }); - sp.setBreakEnabled(true); - QCOMPARE(beProp.value(), true); + beProp.setBinding([&] { return sp.isBreakEnabled(); }); + sp.setBreakEnabled(true); + QCOMPARE(beProp.value(), true); + } else { + // setting isBreakEnabled() will return false and raise an error + const auto currErrorCount = errorChangedSpy.size(); + sp.setBreakEnabled(true); + QCOMPARE(errorProp.value(), QSerialPort::SerialPortError::NotOpenError); + QCOMPARE(errorChangedSpy.size(), currErrorCount + 1); + } } QTEST_MAIN(tst_QSerialPort) |