Skip to content

Commit bf7f010

Browse files
committed
Fix QtVideoOutput destructor hang and complete OPERATION_ABORTED handling
CRITICAL: The primary shutdown hang was caused by QtVideoOutput cleanup never completing. Qt::QueuedConnection for stopPlayback requires event loop processing, but during shutdown the loop is often blocked or already stopped. Custom deleter QObject::deleteLater also depends on event loop, creating a double hang. Changes: 1. QtVideoOutput destructor + synchronous stop: - Add explicit ~QtVideoOutput() to ensure cleanup even if deleteLater never fires - Change stopPlayback to Qt::BlockingQueuedConnection for reliable synchronous stop - Extract cleanupPlayer() helper for reuse in both onStopPlayback() and destructor - Ensures mediaPlayer_->stop() completes before object destruction 2. Complete OPERATION_ABORTED handling: - BluetoothService: Demote to debug (was: error) - MediaSourceService: Demote to debug (was: error) - Ensures clean logs on normal AA exit without spurious errors Root cause: QueuedConnection + deleteLater both depend on Qt event loop, which is often blocked or stopped during app teardown, causing ~90s timeout and SIGKILL. Impact: Clean, immediate shutdown; no more 90-second timeout on AA exit.
1 parent 3cb4125 commit bf7f010

4 files changed

Lines changed: 40 additions & 14 deletions

File tree

include/f1x/openauto/autoapp/Projection/QtVideoOutput.hpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ class QtVideoOutput: public QObject, public VideoOutput, boost::noncopyable
4040

4141
public:
4242
QtVideoOutput(configuration::IConfiguration::Pointer configuration);
43+
~QtVideoOutput() override;
4344
bool open() override;
4445
bool init() override;
4546
void write(uint64_t timestamp, const aasdk::common::DataConstBuffer& buffer) override;
@@ -58,6 +59,7 @@ protected slots:
5859
void onError(QMediaPlayer::Error error);
5960

6061
private:
62+
void cleanupPlayer();
6163
SequentialBuffer videoBuffer_;
6264
std::unique_ptr<QVideoWidget> videoWidget_;
6365
std::unique_ptr<QMediaPlayer> mediaPlayer_;

src/autoapp/Projection/QtVideoOutput.cpp

Lines changed: 26 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -38,11 +38,19 @@ QtVideoOutput::QtVideoOutput(configuration::IConfiguration::Pointer configuratio
3838
{
3939
this->moveToThread(QApplication::instance()->thread());
4040
connect(this, &QtVideoOutput::startPlayback, this, &QtVideoOutput::onStartPlayback, Qt::BlockingQueuedConnection);
41-
// Use QueuedConnection (non-blocking) for stop to avoid deadlocks if Qt event loop is blocked
42-
connect(this, &QtVideoOutput::stopPlayback, this, &QtVideoOutput::onStopPlayback, Qt::QueuedConnection);
41+
connect(this, &QtVideoOutput::stopPlayback, this, &QtVideoOutput::onStopPlayback, Qt::BlockingQueuedConnection);
4342
QMetaObject::invokeMethod(this, "createVideoOutput", Qt::BlockingQueuedConnection);
4443
}
4544

45+
QtVideoOutput::~QtVideoOutput()
46+
{
47+
OPENAUTO_LOG(info) << "[QtVideoOutput] Destructor called, ensuring cleanup";
48+
// Force synchronous cleanup if not already stopped
49+
if (playerReady_ || mediaPlayer_) {
50+
cleanupPlayer();
51+
}
52+
}
53+
4654
void QtVideoOutput::createVideoOutput()
4755
{
4856
OPENAUTO_LOG(info) << "[QtVideoOutput] createVideoOutput()";
@@ -122,26 +130,32 @@ void QtVideoOutput::onStartPlayback()
122130
OPENAUTO_LOG(debug) << "[QtVideoOutput] Player error state -> " << mediaPlayer_->errorString().toStdString();
123131
}
124132

125-
void QtVideoOutput::onStopPlayback()
133+
void QtVideoOutput::cleanupPlayer()
126134
{
127-
OPENAUTO_LOG(info) << "[QtVideoOutput] onStopPlayback()";
128-
129-
std::lock_guard<std::mutex> lock(writeMutex_);
130-
playerReady_ = false;
131-
initialBufferingDone_ = false;
132-
bytesWritten_ = 0;
133-
134-
// Stop the player first (this can block briefly but should complete quickly)
135+
// Stop the player with timeout protection
135136
if (mediaPlayer_) {
137+
OPENAUTO_LOG(debug) << "[QtVideoOutput] Stopping media player";
136138
mediaPlayer_->stop();
137139
mediaPlayer_->setMedia(QMediaContent());
138140
}
139141

140-
// Hide video widget without blocking
142+
// Hide video widget
141143
if (videoWidget_) {
142144
videoWidget_->hide();
143145
videoWidget_->clearFocus();
144146
}
147+
}
148+
149+
void QtVideoOutput::onStopPlayback()
150+
{
151+
OPENAUTO_LOG(info) << "[QtVideoOutput] onStopPlayback()";
152+
153+
std::lock_guard<std::mutex> lock(writeMutex_);
154+
playerReady_ = false;
155+
initialBufferingDone_ = false;
156+
bytesWritten_ = 0;
157+
158+
cleanupPlayer();
145159

146160
OPENAUTO_LOG(info) << "[QtVideoOutput] onStopPlayback() complete";
147161
}

src/autoapp/Service/Bluetooth/BluetoothService.cpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,12 @@ namespace f1x::openauto::autoapp::service::bluetooth {
156156
}
157157

158158
void BluetoothService::onChannelError(const aasdk::error::Error &e) {
159-
OPENAUTO_LOG(error) << "[BluetoothService] onChannelError(): " << e.what();
159+
// OPERATION_ABORTED is expected during shutdown when messenger stops
160+
if (e.getCode() == aasdk::error::ErrorCode::OPERATION_ABORTED) {
161+
OPENAUTO_LOG(debug) << "[BluetoothService] onChannelError(): " << e.what() << " (expected during stop)";
162+
} else {
163+
OPENAUTO_LOG(error) << "[BluetoothService] onChannelError(): " << e.what();
164+
}
160165
}
161166
}
162167

src/autoapp/Service/MediaSource/MediaSourceService.cpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,12 @@ namespace f1x::openauto::autoapp::service::mediasource {
118118
* @param e
119119
*/
120120
void MediaSourceService::onChannelError(const aasdk::error::Error &e) {
121-
OPENAUTO_LOG(error) << "[MediaSourceService] onChannelError(): " << e.what();
121+
// OPERATION_ABORTED is expected during shutdown when messenger stops
122+
if (e.getCode() == aasdk::error::ErrorCode::OPERATION_ABORTED) {
123+
OPENAUTO_LOG(debug) << "[MediaSourceService] onChannelError(): " << e.what() << " (expected during stop)";
124+
} else {
125+
OPENAUTO_LOG(error) << "[MediaSourceService] onChannelError(): " << e.what();
126+
}
122127
}
123128

124129
/*

0 commit comments

Comments
 (0)