feat: Implement timeout handling and background processing for update feature

This commit is contained in:
claudi 2026-01-30 12:09:03 +01:00
parent c97301728c
commit f4eb511a1c
7 changed files with 849 additions and 94 deletions

80
UPDATE_FIX_SUMMARY.md Normal file
View file

@ -0,0 +1,80 @@
# Update Feature Fixes - Final Summary
## Problem Identified
The update feature was causing the application to hang indefinitely when clicked. The issue had two components:
1. **UI Thread Blocking**: The original code was running download operations synchronously on the UI thread
2. **Network Timeout Issues**: Even with timeouts set, the socket-level network calls would hang indefinitely if the server didn't respond
## Solutions Implemented
### 1. Background Threading (First Fix)
- Created `UpdateDownloadWorker` class to run download operations in a background thread
- Moved blocking network calls off the UI thread
- This prevents the UI from freezing while waiting for network operations
### 2. Aggressive Timeout Strategy (Second Fix)
Applied timeouts at multiple levels to ensure the app never hangs:
#### A. Socket-Level Timeout (Most Important)
- **File**: `src/webdrop_bridge/core/updater.py`
- Reduced `urlopen()` timeout from 10 seconds to **5 seconds**
- This is the first line of defense against hanging socket connections
- Applied in `_fetch_release()` method
#### B. Asyncio-Level Timeout
- **File**: `src/webdrop_bridge/ui/main_window.py` and `src/webdrop_bridge/core/updater.py`
- `UpdateCheckWorker`: 10-second timeout on entire check operation
- `UpdateDownloadWorker`: 300-second timeout on download, 30-second on verification
- `check_for_updates()`: 8-second timeout on async executor
- These catch any remaining hangs in the asyncio operations
#### C. Qt-Level Timeout (Final Safety Net)
- **File**: `src/webdrop_bridge/ui/main_window.py`
- Update check: **30-second QTimer** safety timeout (`_run_async_check()`)
- Download: **10-minute QTimer** safety timeout (`_perform_update_async()`)
- If nothing else works, Qt's event loop will forcefully close the operation
### 3. Error Handling Improvements
- Added proper exception handling for `asyncio.TimeoutError`
- Better logging to identify where hangs occur
- User-friendly error messages like "no server response" or "Operation timed out"
- Graceful degradation: operations fail fast instead of hanging
## Timeout Hierarchy (in seconds)
```
Update Check Flow:
QTimer safety net: 30s ─┐
├─ Asyncio timeout: 10s ─┐
├─ Socket timeout: 5s (first to trigger)
Download Flow:
QTimer safety net: 600s ─┐
├─ Asyncio timeout: 300s ─┐
├─ Socket timeout: 5s (first to trigger)
```
## Files Modified
1. **src/webdrop_bridge/ui/main_window.py**
- Updated `UpdateCheckWorker.run()` with timeout handling
- Updated `UpdateDownloadWorker.run()` with timeout handling
- Added QTimer safety timeouts in `_run_async_check()` and `_perform_update_async()`
- Proper event loop cleanup in finally blocks
2. **src/webdrop_bridge/core/updater.py**
- Reduced socket timeout in `_fetch_release()` from 10s to 5s
- Added timeout to `check_for_updates()` async operation
- Added timeout to `download_update()` async operation
- Added timeout to `verify_checksum()` async operation
- Better error logging with exception types
## Testing
- All 7 integration tests pass
- Timeout verification script confirms all timeout mechanisms are in place
- No syntax errors in modified code
## Result
The application will no longer hang indefinitely when checking for or downloading updates. Instead:
- Operations timeout quickly (5-30 seconds depending on operation type)
- User gets clear feedback about what went wrong
- User can retry or cancel without force-killing the app
- Background threads are properly cleaned up to avoid resource leaks