1
0
mirror of https://github.com/clementine-player/Clementine synced 2024-12-18 12:28:31 +01:00

Exit worker processes when their sockets are closed, and make sure the main app closes sockets when exiting - fixes a crash dialog on Windows.

This commit is contained in:
David Sansome 2012-01-08 16:34:34 +00:00
parent 1924107e16
commit 3e0f252b34
6 changed files with 53 additions and 10 deletions

View File

@ -962,3 +962,9 @@ void SpotifyClient::AlbumBrowseComplete(sp_albumbrowse* result, void* userdata)
me->SendMessage(message); me->SendMessage(message);
sp_albumbrowse_release(result); sp_albumbrowse_release(result);
} }
void SpotifyClient::SocketClosed() {
AbstractMessageHandler<pb::spotify::Message>::SocketClosed();
qApp->exit();
}

View File

@ -50,6 +50,7 @@ public:
protected: protected:
void MessageArrived(const pb::spotify::Message& message); void MessageArrived(const pb::spotify::Message& message);
void SocketClosed();
private slots: private slots:
void ProcessEvents(); void ProcessEvents();

View File

@ -21,6 +21,7 @@
#include "core/logging.h" #include "core/logging.h"
#include "core/timeconstants.h" #include "core/timeconstants.h"
#include <QCoreApplication>
#include <QDateTime> #include <QDateTime>
#include <QFileInfo> #include <QFileInfo>
#include <QTextCodec> #include <QTextCodec>
@ -549,3 +550,9 @@ QByteArray TagReaderWorker::LoadEmbeddedArt(const QString& filename) const {
return QByteArray(); return QByteArray();
} }
void TagReaderWorker::SocketClosed() {
AbstractMessageHandler<pb::tagreader::Message>::SocketClosed();
qApp->exit();
}

View File

@ -41,6 +41,7 @@ public:
protected: protected:
void MessageArrived(const pb::tagreader::Message& message); void MessageArrived(const pb::tagreader::Message& message);
void SocketClosed();
private: private:
void ReadFile(const QString& filename, pb::tagreader::SongMetadata* song) const; void ReadFile(const QString& filename, pb::tagreader::SongMetadata* song) const;

View File

@ -63,6 +63,7 @@ template <typename HandlerType>
class WorkerPool : public _WorkerPoolBase { class WorkerPool : public _WorkerPoolBase {
public: public:
WorkerPool(QObject* parent = 0); WorkerPool(QObject* parent = 0);
~WorkerPool();
// Sets the name of the worker executable. This is looked for first in the // Sets the name of the worker executable. This is looked for first in the
// current directory, and then in $PATH. You must call this before calling // current directory, and then in $PATH. You must call this before calling
@ -81,9 +82,8 @@ public:
// Starts all workers. // Starts all workers.
void Start(); void Start();
// Returns a handler in a round-robin fashion. May return NULL if no handlers // Returns a handler in a round-robin fashion. Will block if no handlers are
// are running yet, in which case you must queue the request yourself and // available yet.
// re-send it when the WorkerConnected() signal is emitted.
HandlerType* NextHandler(); HandlerType* NextHandler();
protected: protected:
@ -93,9 +93,11 @@ protected:
private: private:
struct Worker { struct Worker {
Worker() : local_server_(NULL), process_(NULL), handler_(NULL) {} Worker() : local_server_(NULL), local_socket_(NULL), process_(NULL),
handler_(NULL) {}
QLocalServer* local_server_; QLocalServer* local_server_;
QLocalSocket* local_socket_;
QProcess* process_; QProcess* process_;
HandlerType* handler_; HandlerType* handler_;
}; };
@ -144,6 +146,27 @@ WorkerPool<HandlerType>::WorkerPool(QObject* parent)
local_server_name_ = "workerpool"; local_server_name_ = "workerpool";
} }
template <typename HandlerType>
WorkerPool<HandlerType>::~WorkerPool() {
foreach (const Worker& worker, workers_) {
if (worker.local_socket_ && worker.process_) {
// The worker is connected. Close his socket and wait for him to exit.
qLog(Debug) << "Closing worker socket";
worker.local_socket_->close();
worker.process_->waitForFinished(500);
}
if (worker.process_ && worker.process_->state() == QProcess::Running) {
// The worker is still running - kill it.
qLog(Debug) << "Killing worker process";
worker.process_->terminate();
if (!worker.process_->waitForFinished(500)) {
worker.process_->kill();
}
}
}
}
template <typename HandlerType> template <typename HandlerType>
void WorkerPool<HandlerType>::SetWorkerCount(int count) { void WorkerPool<HandlerType>::SetWorkerCount(int count) {
Q_ASSERT(workers_.isEmpty()); Q_ASSERT(workers_.isEmpty());
@ -201,6 +224,7 @@ void WorkerPool<HandlerType>::DoStart() {
template <typename HandlerType> template <typename HandlerType>
void WorkerPool<HandlerType>::StartOneWorker(Worker* worker) { void WorkerPool<HandlerType>::StartOneWorker(Worker* worker) {
DeleteQObjectPointerLater(&worker->local_server_); DeleteQObjectPointerLater(&worker->local_server_);
DeleteQObjectPointerLater(&worker->local_socket_);
DeleteQObjectPointerLater(&worker->process_); DeleteQObjectPointerLater(&worker->process_);
DeleteQObjectPointerLater(&worker->handler_); DeleteQObjectPointerLater(&worker->handler_);
@ -242,15 +266,15 @@ void WorkerPool<HandlerType>::NewConnection() {
qLog(Debug) << "Worker connected to" << server->fullServerName(); qLog(Debug) << "Worker connected to" << server->fullServerName();
// Accept the connection. // Accept the connection.
QLocalSocket* socket = server->nextPendingConnection(); worker->local_socket_ = server->nextPendingConnection();
// We only ever accept one connection per worker, so destroy the server now. // We only ever accept one connection per worker, so destroy the server now.
socket->setParent(this); worker->local_socket_->setParent(this);
worker->local_server_->deleteLater(); worker->local_server_->deleteLater();
worker->local_server_ = NULL; worker->local_server_ = NULL;
// Create the handler. // Create the handler.
worker->handler_ = new HandlerType(socket, this); worker->handler_ = new HandlerType(worker->local_socket_, this);
emit WorkerConnected(); emit WorkerConnected();
} }

View File

@ -370,12 +370,12 @@ int main(int argc, char *argv[]) {
cover_providers.AddProvider(new AmazonCoverProvider); cover_providers.AddProvider(new AmazonCoverProvider);
// Create the tag loader on another thread. // Create the tag loader on another thread.
TagReaderClient tag_reader_client; TagReaderClient* tag_reader_client = new TagReaderClient;
QThread tag_reader_thread; QThread tag_reader_thread;
tag_reader_thread.start(); tag_reader_thread.start();
tag_reader_client.moveToThread(&tag_reader_thread); tag_reader_client->moveToThread(&tag_reader_thread);
tag_reader_client.Start(); tag_reader_client->Start();
// Create some key objects // Create some key objects
scoped_ptr<BackgroundThread<Database> > database( scoped_ptr<BackgroundThread<Database> > database(
@ -436,6 +436,10 @@ int main(int argc, char *argv[]) {
int ret = a.exec(); int ret = a.exec();
tag_reader_client->deleteLater();
tag_reader_thread.quit();
tag_reader_thread.wait();
#ifdef Q_OS_LINUX #ifdef Q_OS_LINUX
// The nvidia driver would cause Clementine (or any application that used // The nvidia driver would cause Clementine (or any application that used
// opengl) to use 100% cpu on shutdown. See: // opengl) to use 100% cpu on shutdown. See: