-
Notifications
You must be signed in to change notification settings - Fork 198
Fix: Prevent UI reset when new device inserted during write (#825) #914
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Fix: Prevent UI reset when new device inserted during write (#825) #914
Conversation
…#825) When a new USB drive was inserted while another drive was being written to, the drive selection would automatically change to the newly inserted drive, causing the progress bar to disappear. The write process continued in the background, but users had no visual feedback. This fix checks if the currently selected drive has an active write operation (progress > 0 and < drive size) before changing the selection. If a write is in progress, the selection remains unchanged when new drives are connected, preserving the progress display. Fixes FedoraQt#825 Signed-off-by: Prachi194agrawal <[email protected]>
|
Hi, thank you for your change. While I believe it fixes the issue, I have a slightly different solution in mind that I would like to apply instead, but it might require a bigger change. I will try to look into this as soon as possible. |
|
Hi @grulja, thanks for the feedback! I understand that you have a different approach in mind which may involve a larger change. If you have a moment, could you briefly explain the direction you’re considering (even at a high level)? That would help me understand the reasoning and see if I can either adjust my current fix or help work on the alternative solution. Thanks again for taking a look! |
|
Without further blocking this, I think there are two possible solutions:
What do you think? @gastoner any opinion? |
|
I would prefer the v2, liking the solution with |
grulja
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please implement the changes as requested in the comment above. Thank you.
…raQt#825) Replace progress-based logic with a cleaner state-based approach by adding an isBusy() method to the Drive class. This encapsulates the busy state within the Drive object itself, making the code more maintainable. Changes: - Add isBusy() getter method and m_isBusy member variable to Drive class - Set m_isBusy = true when write() or restore() operations start - Reset m_isBusy = false when operations complete in onFinished() callbacks - Update DriveManager::onDriveConnected() to check isBusy() instead of progress - Apply changes across all platform implementations (Linux, Mac, Windows) This prevents the UI from resetting when a new USB device is inserted during an active write or restore operation. Fixes FedoraQt#825 Signed-off-by: Prachi194agrawal <[email protected]>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Each implementation [Linux/Mac/Windows]DriveManager has QProcess as a member variable. Can we possibly move this to the Drive itself and remove it in each implementation so it's a common member for all of them. Then you can check the state of the process instead of tracking the state yourself through m_isBusy.
Btw. it looks WinDriveManager already has bool WinDrive::busy() implementation doing exactly that so re-use that and just move it to the common class so all the other implementations have this as well.
- Moved QProcess *m_process to Drive base class (shared by all platforms) - Removed manual m_isBusy boolean flag entirely - Implemented isBusy() to check actual QProcess::state() - Updated all platform implementations (Linux/Mac/Windows) to use inherited m_process - Renamed m_child to m_process in Mac and Windows implementations - Removed all manual busy flag assignments This provides a cleaner, state-based architecture where busy status is determined by the actual process state rather than manual tracking.
|
Sir,
This provides a cleaner, state-based architecture as suggested. All platforms now share the same QProcess management logic, and busy status is determined by the actual process state rather than manual tracking. Ready for review. |
src/app/drivemanager.h
Outdated
| RestoreStatus m_restoreStatus{CLEAN}; | ||
| QString m_error{}; | ||
| bool m_delayedWrite{false}; | ||
| QProcess *m_process{nullptr}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we do here something like:
struct DriveOperationDeleter {
void operator()(QProcess *process) {
if (!process) {
return;
}
if (process->state() != QProcess::NotRunning) {
#ifdef Q_OS_WIN
process->kill();
process->waitForFinished();
#else
process->terminate();
if (!process->waitForFinished(3000)) {
process->kill();
process->waitForFinished();
}
#endif
}
delete process;
}
};
std::unique_ptr<QProcess, DriveOperationDeleter> m_process;
Then you can remove the m_process cleaning that is currently done separately in each [Linux,Mac,Windows]DriveManager destructor. Also you can simplify the process removal in each cancel() method and make the DriveManager::cancel() to simply do m_process.reset() that does exactly what we want thanks to the DriveOperationDeleter. With the use of std::unique_ptr you don't have to do:
if (m_process) {
m_process->deleteLater();
}
m_process = new QProcess(this);
but simply:
m_process = std::make_unique<QProcess>(this);
Implement mentor's suggestion to use RAII (Resource Acquisition Is Initialization) with std::unique_ptr and a custom deleter for process management. This modernizes the codebase and eliminates manual memory management issues. Changes: - Added DriveOperationDeleter struct with platform-specific cleanup logic - Changed m_process from raw QProcess* to std::unique_ptr with custom deleter - Removed all manual deleteLater() and nullptr assignments - Simplified cancel() methods across all Drive implementations - Reduced boilerplate code by ~30 lines across platforms Benefits: - Automatic cleanup prevents memory leaks even with exceptions - Centralized cleanup logic in one place (DriveOperationDeleter) - Safer and more maintainable code following modern C++ practices - Consistent behavior across Windows, Linux, and macOS platforms The DriveOperationDeleter handles: - Windows: kill() then waitForFinished() - Unix/Mac: terminate() with 3-second grace period, then kill() if needed This addresses the mentor's feedback on PR FedoraQt#914. Signed-off-by: Prachi194agrawal <[email protected]>
Implement mentor's suggestion to use RAII (Resource Acquisition Is Initialization) with std::unique_ptr and a custom deleter for process management. This modernizes the codebase and eliminates manual memory management issues. Changes: - Added DriveOperationDeleter struct with platform-specific cleanup logic - Changed m_process from raw QProcess* to std::unique_ptr with custom deleter - Removed all manual deleteLater() and nullptr assignments - Simplified cancel() methods across all Drive implementations - Reduced boilerplate code by ~30 lines across platforms Benefits: - Automatic cleanup prevents memory leaks even with exceptions - Centralized cleanup logic in one place (DriveOperationDeleter) - Safer and more maintainable code following modern C++ practices - Consistent behavior across Windows, Linux, and macOS platforms The DriveOperationDeleter handles: - Windows: kill() then waitForFinished() - Unix/Mac: terminate() with 3-second grace period, then kill() if needed This addresses the mentor's feedback on PR FedoraQt#914. Signed-off-by: Prachi194agrawal <[email protected]>
7990cf9 to
9f5aa75
Compare
grulja
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Almost there :)
src/app/drivemanager.cpp
Outdated
| return; | ||
| } | ||
|
|
||
| // Standard logic: Select the new drive if not busy |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: this comment can be removed
src/app/linuxdrivemanager.cpp
Outdated
| m_process = nullptr; | ||
| m_image = nullptr; | ||
| } | ||
| // Process cleanup handled automatically by unique_ptr deleter |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here we should also do m_process.reset().
src/app/linuxdrivemanager.cpp
Outdated
| m_process->deleteLater(); | ||
| m_process = nullptr; | ||
| } | ||
| // Process cleanup handled automatically by unique_ptr deleter |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here we should also do m_process.reset().
src/app/linuxdrivemanager.cpp
Outdated
| m_image->setErrorString(errorMessage); | ||
| m_process->deleteLater(); | ||
| m_process = nullptr; | ||
| // Process cleanup handled automatically by unique_ptr deleter |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here we should also do m_process.reset().
src/app/windrivemanager.cpp
Outdated
| { | ||
| if (m_child) | ||
| m_child->terminate(); | ||
| // Process cleanup is handled automatically by std::unique_ptr with DriveOperationDeleter |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think all platform specific destructors can be removed. You can do ~WinDrive() = default in the header.
src/app/windrivemanager.cpp
Outdated
| m_child = nullptr; | ||
| } | ||
| // Reset the unique_ptr, which automatically invokes DriveOperationDeleter | ||
| m_process.reset(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The m_process.reset() should be moved to Drive::cancel() so we don't have to do in each platform implementation.
src/app/linuxdrivemanager.cpp
Outdated
| m_process->deleteLater(); | ||
| m_process = nullptr; | ||
| // Reset the unique_ptr, which automatically invokes DriveOperationDeleter | ||
| m_process.reset(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can be removed in favor of generic implementation in Drive::cancel().
- Remove redundant comment in drivemanager.cpp - Move m_process.reset() to base Drive::cancel() method - Remove duplicate reset() calls from platform-specific cancel() methods - Add explicit reset() calls in LinuxDrive completion handlers - Simplify WinDrive destructor to use default - Remove all implementation comments
| if (!m_process) | ||
| m_process = new QProcess(this); | ||
| // Create new process using unique_ptr | ||
| m_process.reset(new QProcess(this)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
→ m_process = std::make_unique<QProcess>(this);
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And also remove the comment.
src/app/linuxdrivemanager.cpp
Outdated
| @@ -250,7 +250,7 @@ | |||
| { | |||
| Drive::cancel(); | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't know why only the LinuxDrive does something extra in the reimplementation of cancel() but I think we can remove it altogether so none of the cancel() reimplementations exists. It wouldn't work this way anyway, because calling Drive::cancel() first it clears the m_process so the condition that is checked below will never be satisfied.
|
|
||
| if (!m_process) | ||
| m_process = new QProcess(this); | ||
| m_process.reset(new QProcess(this)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Again, use std::make_unique
src/app/macdrivemanager.cpp
Outdated
| return true; | ||
| } | ||
|
|
||
| void MacDrive::cancel() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove MacDrive::cancel() completely. If we don't implement it ourself the base DriveManager::cancel() will be called.
| m_child->deleteLater(); | ||
|
|
||
| m_child = new QProcess(this); | ||
| m_process.reset(new QProcess(this)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
std::make_unique
| connect(m_child, &QProcess::readyRead, this, &WinDrive::onReadyRead); | ||
| connect(qApp, &QCoreApplication::aboutToQuit, m_child, &QProcess::terminate); | ||
| // Create new process using unique_ptr | ||
| m_process.reset(new QProcess(this)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
std::make_unique
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And drop the comment.
| connect(m_child, &QProcess::readyRead, this, &MacDrive::onReadyRead); | ||
| connect(qApp, &QCoreApplication::aboutToQuit, m_child, &QProcess::terminate); | ||
| // Create new process using unique_ptr | ||
| m_process.reset(new QProcess(this)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
std::make_unique and drop the comment
|
|
||
| m_child = new QProcess(this); | ||
|
|
||
| m_process.reset(new QProcess(this)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
std::make_unique
src/app/windrivemanager.h
Outdated
| public: | ||
| WinDrive(WinDriveProvider *parent, const QString &name, uint64_t size, bool containsLive, int device, const QString &serialNumber); | ||
| ~WinDrive(); | ||
| ~WinDrive() = default; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry, I was wrong before, it's better not to define destructor at all in this case.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And you can also remove the destructor from LinuxDriveManager
…orm-specific cancel() overrides
|
Sir, I've updated the PR to use std::make_unique and moved the cleanup logic to the base class as suggested—thanks for the helpful guidance. Ready for another review. |
grulja
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good to me. Thanks.
I will do some quick testing later today before I merge this to be sure it works as expected.
grulja
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry, you might have to revert the usage of std::make_unique as it doesn't work with custom deleter, which I didn't realize (it fails to build).
d27ef2e to
56c55a7
Compare
|
Can you please fix the clang-format issue? You can run clang-format locally to fix it properly. I will get to this next Friday as I will be now on vacation until then. |
Yes, sir. |
When a new USB drive was inserted while another drive was being written to, the drive selection would automatically change to the newly inserted drive, causing the progress bar to disappear. The write process continued in the background, but users had no visual feedback.
This fix checks if the currently selected drive has an active write operation (progress > 0 and < drive size) before changing the selection. If a write is in progress, the selection remains unchanged when new drives are connected, preserving the progress display.
Fixes #825