From 9041117867b081c978f1e6122aa2a67e77786d6b Mon Sep 17 00:00:00 2001 From: David Sansome Date: Thu, 5 Jan 2012 23:22:51 +0000 Subject: [PATCH] Move everything around in the source tree - static libraries and external binaries now go in "ext/". Add a generic worker pool. --- CMakeLists.txt | 9 +- .../clementine-spotifyblob}/CMakeLists.txt | 9 +- .../clementine-spotifyblob}/main.cpp | 0 .../clementine-spotifyblob}/mediapipeline.cpp | 0 .../clementine-spotifyblob}/mediapipeline.h | 0 .../spotify_utilities.cpp | 0 .../spotify_utilities.h | 0 .../spotify_utilities.mm | 0 .../clementine-spotifyblob}/spotifyclient.cpp | 0 .../clementine-spotifyblob}/spotifyclient.h | 0 .../clementine-spotifyblob}/spotifykey.h | 0 .../clementine-tagreader}/CMakeLists.txt | 10 +- .../clementine-tagreader}/fmpsparser.cpp | 0 .../clementine-tagreader}/fmpsparser.h | 0 .../clementine-tagreader}/main.cpp | 0 .../clementine-tagreader}/tagreaderworker.cpp | 0 .../clementine-tagreader}/tagreaderworker.h | 2 +- ext/libclementine-common/CMakeLists.txt | 39 +++ .../libclementine-common}/core/closure.cpp | 0 .../libclementine-common}/core/closure.h | 0 .../libclementine-common}/core/encoding.cpp | 0 .../libclementine-common}/core/encoding.h | 0 .../libclementine-common}/core/logging.cpp | 0 .../libclementine-common}/core/logging.h | 0 .../core}/messagehandler.cpp | 0 .../core}/messagehandler.h | 11 +- ext/libclementine-common/core/workerpool.cpp | 25 ++ ext/libclementine-common/core/workerpool.h | 276 ++++++++++++++++++ .../libclementine-spotifyblob}/CMakeLists.txt | 12 +- .../blobversion.h.in | 0 .../spotifymessagehandler.cpp | 0 .../spotifymessagehandler.h | 0 .../spotifymessages.proto | 0 ext/libclementine-tagreader/CMakeLists.txt | 17 ++ .../tagreadermessages.proto | 0 src/CMakeLists.txt | 10 +- src/core/commandlineoptions.cpp | 2 +- src/core/multisortfilterproxy.cpp | 2 +- src/core/song.cpp | 6 +- src/core/tagreaderclient.cpp | 23 +- src/core/tagreaderclient.h | 15 +- src/main.cpp | 7 +- src/ui/mainwindow.cpp | 2 + src/ui/mainwindow.h | 3 + tagreader/common/CMakeLists.txt | 41 --- 45 files changed, 433 insertions(+), 88 deletions(-) rename {spotifyblob/blob => ext/clementine-spotifyblob}/CMakeLists.txt (85%) rename {spotifyblob/blob => ext/clementine-spotifyblob}/main.cpp (100%) rename {spotifyblob/blob => ext/clementine-spotifyblob}/mediapipeline.cpp (100%) rename {spotifyblob/blob => ext/clementine-spotifyblob}/mediapipeline.h (100%) rename {spotifyblob/blob => ext/clementine-spotifyblob}/spotify_utilities.cpp (100%) rename {spotifyblob/blob => ext/clementine-spotifyblob}/spotify_utilities.h (100%) rename {spotifyblob/blob => ext/clementine-spotifyblob}/spotify_utilities.mm (100%) rename {spotifyblob/blob => ext/clementine-spotifyblob}/spotifyclient.cpp (100%) rename {spotifyblob/blob => ext/clementine-spotifyblob}/spotifyclient.h (100%) rename {spotifyblob/blob => ext/clementine-spotifyblob}/spotifykey.h (100%) rename {tagreader/tagreader => ext/clementine-tagreader}/CMakeLists.txt (76%) rename {tagreader/tagreader => ext/clementine-tagreader}/fmpsparser.cpp (100%) rename {tagreader/tagreader => ext/clementine-tagreader}/fmpsparser.h (100%) rename {tagreader/tagreader => ext/clementine-tagreader}/main.cpp (100%) rename {tagreader/tagreader => ext/clementine-tagreader}/tagreaderworker.cpp (100%) rename {tagreader/tagreader => ext/clementine-tagreader}/tagreaderworker.h (98%) create mode 100644 ext/libclementine-common/CMakeLists.txt rename {src => ext/libclementine-common}/core/closure.cpp (100%) rename {src => ext/libclementine-common}/core/closure.h (100%) rename {src => ext/libclementine-common}/core/encoding.cpp (100%) rename {src => ext/libclementine-common}/core/encoding.h (100%) rename {src => ext/libclementine-common}/core/logging.cpp (100%) rename {src => ext/libclementine-common}/core/logging.h (100%) rename {tagreader/common => ext/libclementine-common/core}/messagehandler.cpp (100%) rename {tagreader/common => ext/libclementine-common/core}/messagehandler.h (96%) create mode 100644 ext/libclementine-common/core/workerpool.cpp create mode 100644 ext/libclementine-common/core/workerpool.h rename {spotifyblob/common => ext/libclementine-spotifyblob}/CMakeLists.txt (64%) rename {spotifyblob/common => ext/libclementine-spotifyblob}/blobversion.h.in (100%) rename {spotifyblob/common => ext/libclementine-spotifyblob}/spotifymessagehandler.cpp (100%) rename {spotifyblob/common => ext/libclementine-spotifyblob}/spotifymessagehandler.h (100%) rename {spotifyblob/common => ext/libclementine-spotifyblob}/spotifymessages.proto (100%) create mode 100644 ext/libclementine-tagreader/CMakeLists.txt rename {tagreader/common => ext/libclementine-tagreader}/tagreadermessages.proto (100%) delete mode 100644 tagreader/common/CMakeLists.txt diff --git a/CMakeLists.txt b/CMakeLists.txt index 6326119cf..9037185d9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -366,8 +366,9 @@ add_subdirectory(3rdparty/universalchardet) add_subdirectory(tests) add_subdirectory(dist) add_subdirectory(tools/ultimate_lyrics_parser) -add_subdirectory(tagreader/common) -add_subdirectory(tagreader/tagreader) +add_subdirectory(ext/libclementine-common) +add_subdirectory(ext/libclementine-tagreader) +add_subdirectory(ext/clementine-tagreader) option(WITH_DEBIAN OFF) if(WITH_DEBIAN) @@ -383,11 +384,11 @@ if(HAVE_BREAKPAD) endif(HAVE_BREAKPAD) if(HAVE_SPOTIFY) - add_subdirectory(spotifyblob/common) + add_subdirectory(ext/libclementine-spotifyblob) endif(HAVE_SPOTIFY) if(HAVE_SPOTIFY_BLOB) - add_subdirectory(spotifyblob/blob) + add_subdirectory(ext/clementine-spotifyblob) endif(HAVE_SPOTIFY_BLOB) # This goes after everything else because KDE fucks everything else up with its diff --git a/spotifyblob/blob/CMakeLists.txt b/ext/clementine-spotifyblob/CMakeLists.txt similarity index 85% rename from spotifyblob/blob/CMakeLists.txt rename to ext/clementine-spotifyblob/CMakeLists.txt index f0384cf91..15a9c8dcc 100644 --- a/spotifyblob/blob/CMakeLists.txt +++ b/ext/clementine-spotifyblob/CMakeLists.txt @@ -1,8 +1,10 @@ include_directories(${SPOTIFY_INCLUDE_DIRS}) include_directories(${PROTOBUF_INCLUDE_DIRS}) include_directories(${CMAKE_CURRENT_BINARY_DIR}) -include_directories(${CMAKE_CURRENT_BINARY_DIR}/../common) -include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../common) +include_directories(${CMAKE_CURRENT_SOURCE_DIR}) +include_directories(${CMAKE_BINARY_DIR}/ext/libclementine-spotifyblob) +include_directories(${CMAKE_SOURCE_DIR}/ext/libclementine-spotifyblob) +include_directories(${CMAKE_SOURCE_DIR}/ext/libclementine-common) include_directories(${CMAKE_SOURCE_DIR}/src) link_directories(${SPOTIFY_LIBRARY_DIRS}) @@ -14,8 +16,6 @@ set(SOURCES mediapipeline.cpp spotifyclient.cpp spotify_utilities.cpp - - ${CMAKE_SOURCE_DIR}/src/core/logging.cpp ) set(HEADERS @@ -45,6 +45,7 @@ target_link_libraries(clementine-spotifyblob ${GSTREAMER_BASE_LIBRARIES} ${GSTREAMER_APP_LIBRARIES} clementine-spotifyblob-messages + libclementine-common ) if(APPLE) diff --git a/spotifyblob/blob/main.cpp b/ext/clementine-spotifyblob/main.cpp similarity index 100% rename from spotifyblob/blob/main.cpp rename to ext/clementine-spotifyblob/main.cpp diff --git a/spotifyblob/blob/mediapipeline.cpp b/ext/clementine-spotifyblob/mediapipeline.cpp similarity index 100% rename from spotifyblob/blob/mediapipeline.cpp rename to ext/clementine-spotifyblob/mediapipeline.cpp diff --git a/spotifyblob/blob/mediapipeline.h b/ext/clementine-spotifyblob/mediapipeline.h similarity index 100% rename from spotifyblob/blob/mediapipeline.h rename to ext/clementine-spotifyblob/mediapipeline.h diff --git a/spotifyblob/blob/spotify_utilities.cpp b/ext/clementine-spotifyblob/spotify_utilities.cpp similarity index 100% rename from spotifyblob/blob/spotify_utilities.cpp rename to ext/clementine-spotifyblob/spotify_utilities.cpp diff --git a/spotifyblob/blob/spotify_utilities.h b/ext/clementine-spotifyblob/spotify_utilities.h similarity index 100% rename from spotifyblob/blob/spotify_utilities.h rename to ext/clementine-spotifyblob/spotify_utilities.h diff --git a/spotifyblob/blob/spotify_utilities.mm b/ext/clementine-spotifyblob/spotify_utilities.mm similarity index 100% rename from spotifyblob/blob/spotify_utilities.mm rename to ext/clementine-spotifyblob/spotify_utilities.mm diff --git a/spotifyblob/blob/spotifyclient.cpp b/ext/clementine-spotifyblob/spotifyclient.cpp similarity index 100% rename from spotifyblob/blob/spotifyclient.cpp rename to ext/clementine-spotifyblob/spotifyclient.cpp diff --git a/spotifyblob/blob/spotifyclient.h b/ext/clementine-spotifyblob/spotifyclient.h similarity index 100% rename from spotifyblob/blob/spotifyclient.h rename to ext/clementine-spotifyblob/spotifyclient.h diff --git a/spotifyblob/blob/spotifykey.h b/ext/clementine-spotifyblob/spotifykey.h similarity index 100% rename from spotifyblob/blob/spotifykey.h rename to ext/clementine-spotifyblob/spotifykey.h diff --git a/tagreader/tagreader/CMakeLists.txt b/ext/clementine-tagreader/CMakeLists.txt similarity index 76% rename from tagreader/tagreader/CMakeLists.txt rename to ext/clementine-tagreader/CMakeLists.txt index 7bd153312..77b07ae4a 100644 --- a/tagreader/tagreader/CMakeLists.txt +++ b/ext/clementine-tagreader/CMakeLists.txt @@ -1,11 +1,10 @@ include_directories(${PROTOBUF_INCLUDE_DIRS}) +include_directories(${CMAKE_CURRENT_SOURCE_DIR}) include_directories(${CMAKE_CURRENT_BINARY_DIR}) -include_directories(${CMAKE_CURRENT_BINARY_DIR}/../common) -include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../common) +include_directories(${CMAKE_SOURCE_DIR}/ext/libclementine-common) +include_directories(${CMAKE_BINARY_DIR}/ext/libclementine-tagreader) include_directories(${CMAKE_SOURCE_DIR}/src) -link_directories(${SPOTIFY_LIBRARY_DIRS}) - set(EXECUTABLE_OUTPUT_PATH ${CMAKE_BINARY_DIR}) set(SOURCES @@ -28,7 +27,8 @@ target_link_libraries(clementine-tagreader ${TAGLIB_LIBRARIES} ${QT_QTCORE_LIBRARY} ${QT_QTNETWORK_LIBRARY} - clementine-tagreader-common + libclementine-common + libclementine-tagreader ) if(APPLE) diff --git a/tagreader/tagreader/fmpsparser.cpp b/ext/clementine-tagreader/fmpsparser.cpp similarity index 100% rename from tagreader/tagreader/fmpsparser.cpp rename to ext/clementine-tagreader/fmpsparser.cpp diff --git a/tagreader/tagreader/fmpsparser.h b/ext/clementine-tagreader/fmpsparser.h similarity index 100% rename from tagreader/tagreader/fmpsparser.h rename to ext/clementine-tagreader/fmpsparser.h diff --git a/tagreader/tagreader/main.cpp b/ext/clementine-tagreader/main.cpp similarity index 100% rename from tagreader/tagreader/main.cpp rename to ext/clementine-tagreader/main.cpp diff --git a/tagreader/tagreader/tagreaderworker.cpp b/ext/clementine-tagreader/tagreaderworker.cpp similarity index 100% rename from tagreader/tagreader/tagreaderworker.cpp rename to ext/clementine-tagreader/tagreaderworker.cpp diff --git a/tagreader/tagreader/tagreaderworker.h b/ext/clementine-tagreader/tagreaderworker.h similarity index 98% rename from tagreader/tagreader/tagreaderworker.h rename to ext/clementine-tagreader/tagreaderworker.h index a5b94c355..8bccd7fa8 100644 --- a/tagreader/tagreader/tagreaderworker.h +++ b/ext/clementine-tagreader/tagreaderworker.h @@ -18,8 +18,8 @@ #ifndef TAGREADERWORKER_H #define TAGREADERWORKER_H -#include "messagehandler.h" #include "tagreadermessages.pb.h" +#include "core/messagehandler.h" #include diff --git a/ext/libclementine-common/CMakeLists.txt b/ext/libclementine-common/CMakeLists.txt new file mode 100644 index 000000000..1096d969a --- /dev/null +++ b/ext/libclementine-common/CMakeLists.txt @@ -0,0 +1,39 @@ +include_directories(${PROTOBUF_INCLUDE_DIRS}) +include_directories(${CMAKE_CURRENT_BINARY_DIR}) +include_directories(${CMAKE_CURRENT_SOURCE_DIR}) +include_directories(${CMAKE_SOURCE_DIR}/src) + +set(SOURCES + core/closure.cpp + core/encoding.cpp + core/logging.cpp + core/messagehandler.cpp + core/workerpool.cpp +) + +set(HEADERS + core/closure.h + core/messagehandler.h + core/workerpool.h +) + +qt4_wrap_cpp(MOC ${HEADERS}) + +add_library(libclementine-common STATIC + ${SOURCES} + ${MOC} +) + +# Use protobuf-lite if it's available +if(PROTOBUF_LITE_LIBRARY AND USE_PROTOBUF_LITE) + set(protobuf ${PROTOBUF_LITE_LIBRARY}) +else(PROTOBUF_LITE_LIBRARY AND USE_PROTOBUF_LITE) + set(protobuf ${PROTOBUF_LIBRARY}) +endif(PROTOBUF_LITE_LIBRARY AND USE_PROTOBUF_LITE) + +target_link_libraries(libclementine-common + ${protobuf} + ${CMAKE_THREAD_LIBS_INIT} + chardet +) + diff --git a/src/core/closure.cpp b/ext/libclementine-common/core/closure.cpp similarity index 100% rename from src/core/closure.cpp rename to ext/libclementine-common/core/closure.cpp diff --git a/src/core/closure.h b/ext/libclementine-common/core/closure.h similarity index 100% rename from src/core/closure.h rename to ext/libclementine-common/core/closure.h diff --git a/src/core/encoding.cpp b/ext/libclementine-common/core/encoding.cpp similarity index 100% rename from src/core/encoding.cpp rename to ext/libclementine-common/core/encoding.cpp diff --git a/src/core/encoding.h b/ext/libclementine-common/core/encoding.h similarity index 100% rename from src/core/encoding.h rename to ext/libclementine-common/core/encoding.h diff --git a/src/core/logging.cpp b/ext/libclementine-common/core/logging.cpp similarity index 100% rename from src/core/logging.cpp rename to ext/libclementine-common/core/logging.cpp diff --git a/src/core/logging.h b/ext/libclementine-common/core/logging.h similarity index 100% rename from src/core/logging.h rename to ext/libclementine-common/core/logging.h diff --git a/tagreader/common/messagehandler.cpp b/ext/libclementine-common/core/messagehandler.cpp similarity index 100% rename from tagreader/common/messagehandler.cpp rename to ext/libclementine-common/core/messagehandler.cpp diff --git a/tagreader/common/messagehandler.h b/ext/libclementine-common/core/messagehandler.h similarity index 96% rename from tagreader/common/messagehandler.h rename to ext/libclementine-common/core/messagehandler.h index 22bc6866c..5124d14fe 100644 --- a/tagreader/common/messagehandler.h +++ b/ext/libclementine-common/core/messagehandler.h @@ -67,6 +67,8 @@ protected: template class MessageReply : public _MessageReplyBase { public: + MessageReply(int id, QObject* parent); + const MessageType& message() const { return message_; } void SetReply(const MessageType& message); @@ -88,7 +90,7 @@ public: protected slots: void WriteMessage(const QByteArray& data); void DeviceReadyRead(); - virtual bool SocketClosed() {} + virtual void SocketClosed() {} protected: virtual bool MessageArrived(const QByteArray& data) = 0; @@ -145,6 +147,7 @@ protected: // _MessageHandlerBase bool MessageArrived(const QByteArray& data); + void SocketClosed(); private: QMutex mutex_; @@ -245,6 +248,12 @@ void AbstractMessageHandler::SocketClosed() { pending_replies_.clear(); } +template +MessageReply::MessageReply(int id, QObject* parent) + : _MessageReplyBase(id, parent) +{ +} + template void MessageReply::SetReply(const MessageType& message) { Q_ASSERT(!finished_); diff --git a/ext/libclementine-common/core/workerpool.cpp b/ext/libclementine-common/core/workerpool.cpp new file mode 100644 index 000000000..d3378eb61 --- /dev/null +++ b/ext/libclementine-common/core/workerpool.cpp @@ -0,0 +1,25 @@ +/* This file is part of Clementine. + Copyright 2011, David Sansome + + Clementine is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Clementine is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Clementine. If not, see . +*/ + +#include "workerpool.h" + +_WorkerPoolBase::_WorkerPoolBase(QObject* parent) + : QObject(parent) +{ +} + + diff --git a/ext/libclementine-common/core/workerpool.h b/ext/libclementine-common/core/workerpool.h new file mode 100644 index 000000000..8ebaee578 --- /dev/null +++ b/ext/libclementine-common/core/workerpool.h @@ -0,0 +1,276 @@ +/* This file is part of Clementine. + Copyright 2011, David Sansome + + Clementine is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Clementine is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Clementine. If not, see . +*/ + +#ifndef WORKERPOOL_H +#define WORKERPOOL_H + +#include +#include +#include +#include +#include +#include + +#include "core/closure.h" + + +// Base class containing signals and slots - required because moc doesn't do +// templated objects. +class _WorkerPoolBase : public QObject { + Q_OBJECT + +public: + _WorkerPoolBase(QObject* parent = 0); + +signals: + // Emitted when a worker failed to start. This usually happens when the + // worker wasn't found, or couldn't be executed. + void WorkerFailedToStart(); + + // A worker connected and a handler was created for it. The next call to + // NextHandler() won't return NULL. + void WorkerConnected(); + +protected slots: + virtual void NewConnection() {} + virtual void ProcessError(QProcess::ProcessError) {} +}; + + +// Manages a pool of one or more external processes. A local socket server is +// started for each process, and the address is passed to the process as +// argv[1]. The process is expected to connect back to the socket server, and +// when it does a HandlerType is created for it. +template +class WorkerPool : public _WorkerPoolBase { +public: + WorkerPool(QObject* parent = 0); + + // 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 + // Start(). + void SetExecutableName(const QString& executable_name); + + // Sets the number of worker process to use. Defaults to + // 1 <= (processors / 2) <= 2. + void SetWorkerCount(int count); + + // Sets the prefix to use for the local server (on unix this is a named pipe + // in /tmp). Defaults to QApplication::applicationName(). A random number + // is appended to this name when creating each server. + void SetLocalServerName(const QString& local_server_name); + + // Starts all workers. + void Start(); + + // Returns a handler in a round-robin fashion. May return NULL if no handlers + // are running yet, in which case you must queue the request yourself and + // re-send it when the WorkerConnected() signal is emitted. + HandlerType* NextHandler(); + +protected: + void NewConnection(); + void ProcessError(QProcess::ProcessError error); + +private: + struct Worker { + Worker() : local_server_(NULL), process_(NULL), handler_(NULL) {} + + QLocalServer* local_server_; + QProcess* process_; + HandlerType* handler_; + }; + + void StartOneWorker(Worker* worker); + + template + Worker* FindWorker(T Worker::*member, T value) { + foreach (Worker& worker, workers_) { + if (worker.*member == value) { + return &worker; + } + } + return NULL; + } + + void DeleteQObjectPointerLater(QObject** p) { + if (*p) { + (*p)->deleteLater(); + *p = NULL; + } + } + +private: + QString local_server_name_; + QString executable_name_; + QString executable_path_; + + int worker_count_; + int next_worker_; + QList workers_; +}; + + +template +WorkerPool::WorkerPool(QObject* parent) + : _WorkerPoolBase(parent), + next_worker_(0) +{ + worker_count_ = qBound(1, QThread::idealThreadCount() / 2, 2); + local_server_name_ = qApp->applicationName().toLower(); + + if (local_server_name_.isEmpty()) + local_server_name_ = "workerpool"; +} + +template +void WorkerPool::SetWorkerCount(int count) { + Q_ASSERT(workers_.isEmpty()); + worker_count_ = count; +} + +template +void WorkerPool::SetLocalServerName(const QString& local_server_name) { + Q_ASSERT(workers_.isEmpty()); + local_server_name_ = local_server_name; +} + +template +void WorkerPool::SetExecutableName(const QString& executable_name) { + Q_ASSERT(workers_.isEmpty()); + executable_name_ = executable_name; +} + +template +void WorkerPool::Start() { + Q_ASSERT(workers_.isEmpty()); + + // Find the executable if we can, default to searching $PATH + executable_path_ = executable_name_; + + QStringList search_path; + search_path << qApp->applicationDirPath(); +#ifdef Q_OS_MAC + search_path << qApp->applicationDirPath() + "/../PlugIns"; +#endif + + foreach (const QString& path_prefix, search_path) { + const QString executable_path = path_prefix + "/" + executable_name_; + if (QFile::exists(executable_path)) { + executable_path_ = executable_path; + break; + } + } + + // Start all the workers + for (int i=0 ; i +void WorkerPool::StartOneWorker(Worker* worker) { + DeleteQObjectPointerLater(&worker->local_server_); + DeleteQObjectPointerLater(&worker->process_); + DeleteQObjectPointerLater(&worker->handler_); + + worker->local_server_ = new QLocalServer(this); + worker->process_ = new QProcess(this); + + connect(worker->local_server_, SIGNAL(newConnection()), SLOT(NewConnection())); + connect(worker->process_, SIGNAL(error(QProcess::ProcessError)), + SLOT(ProcessError(QProcess::ProcessError))); + + // Create a server, find an unused name and start listening + forever { + const int unique_number = qrand() ^ reinterpret_cast(this); + const QString name = QString("%1_%2").arg(local_server_name_).arg(unique_number); + + if (worker->local_server_->listen(name)) { + break; + } + } + + // Start the process + worker->process_->setProcessChannelMode(QProcess::ForwardedChannels); + worker->process_->start(executable_path_, + QStringList() << worker->socket_server_->fullServerName()); +} + +template +void WorkerPool::NewConnection() { + QLocalServer* server = qobject_cast(sender()); + + // Find the worker with this server. + Worker* worker = FindWorker(&Worker::local_server_, server); + if (!worker) + return; + + // Accept the connection. + QLocalSocket* socket = server->nextPendingConnection(); + + // We only ever accept one connection per worker, so destroy the server now. + server->deleteLater(); + worker->local_server_ = NULL; + + // Create the handler. + worker->handler_ = new HandlerType(socket, this); + + emit WorkerConnected(); +} + +template +void WorkerPool::ProcessError(QProcess::ProcessError error) { + QProcess* process = qobject_cast(sender()); + + // Find the worker with this process. + Worker* worker = FindWorker(&Worker::process_, process); + if (!worker) + return; + + switch (error) { + case QProcess::FailedToStart: + // Failed to start errors are bad - it usually means the worker isn't + // installed. Don't restart the process, but tell our owner, who will + // probably want to do something fatal. + emit WorkerFailedToStart(); + break; + + default: + // On any other error we just restart the process. + StartOneWorker(worker); + break; + } +} + +template +HandlerType* WorkerPool::NextHandler() { + for (int i=0 ; i #include diff --git a/src/core/multisortfilterproxy.cpp b/src/core/multisortfilterproxy.cpp index 04816f2b7..903e58072 100644 --- a/src/core/multisortfilterproxy.cpp +++ b/src/core/multisortfilterproxy.cpp @@ -1,5 +1,5 @@ -#include "logging.h" #include "multisortfilterproxy.h" +#include "core/logging.h" #include #include diff --git a/src/core/song.cpp b/src/core/song.cpp index f5b44f2c7..12cbbbf96 100644 --- a/src/core/song.cpp +++ b/src/core/song.cpp @@ -15,12 +15,12 @@ along with Clementine. If not, see . */ -#include "encoding.h" -#include "logging.h" #include "mpris_common.h" #include "song.h" #include "timeconstants.h" -#include "tagreader/common/messagehandler.h" +#include "core/encoding.h" +#include "core/logging.h" +#include "core/messagehandler.h" #include diff --git a/src/core/tagreaderclient.cpp b/src/core/tagreaderclient.cpp index 4a900f451..70163f717 100644 --- a/src/core/tagreaderclient.cpp +++ b/src/core/tagreaderclient.cpp @@ -17,21 +17,22 @@ #include "tagreaderclient.h" +#include +#include #include +#include + + +const char* TagReaderClient::kWorkerExecutableName = "clementine-tagreader"; TagReaderClient::TagReaderClient(QObject* parent) : QObject(parent), - process_(NULL), - handler_(NULL) + worker_pool_(new WorkerPool(this)) { } void TagReaderClient::Start() { - delete process_; - delete handler_; - - process_ = new QProcess(this); - process_->start(); + worker_pool_->Start(); } TagReaderReply* TagReaderClient::ReadFile(const QString& filename) { @@ -40,7 +41,7 @@ TagReaderReply* TagReaderClient::ReadFile(const QString& filename) { req->set_filename(DataCommaSizeFromQString(filename)); - return SendMessageWithReply(&message); + return handler_->SendMessageWithReply(&message); } TagReaderReply* TagReaderClient::SaveFile(const QString& filename, const Song& metadata) { @@ -50,7 +51,7 @@ TagReaderReply* TagReaderClient::SaveFile(const QString& filename, const Song& m req->set_filename(DataCommaSizeFromQString(filename)); metadata.ToProtobuf(req->mutable_metadata()); - return SendMessageWithReply(&message); + return handler_->SendMessageWithReply(&message); } TagReaderReply* TagReaderClient::IsMediaFile(const QString& filename) { @@ -59,7 +60,7 @@ TagReaderReply* TagReaderClient::IsMediaFile(const QString& filename) { req->set_filename(DataCommaSizeFromQString(filename)); - return SendMessageWithReply(&message); + return handler_->SendMessageWithReply(&message); } TagReaderReply* TagReaderClient::LoadEmbeddedArt(const QString& filename) { @@ -68,5 +69,5 @@ TagReaderReply* TagReaderClient::LoadEmbeddedArt(const QString& filename) { req->set_filename(DataCommaSizeFromQString(filename)); - return SendMessageWithReply(&message); + return handler_->SendMessageWithReply(&message); } diff --git a/src/core/tagreaderclient.h b/src/core/tagreaderclient.h index 6e384e5ac..9c7dca983 100644 --- a/src/core/tagreaderclient.h +++ b/src/core/tagreaderclient.h @@ -18,10 +18,14 @@ #ifndef TAGREADERCLIENT_H #define TAGREADERCLIENT_H -#include "messagehandler.h" #include "song.h" #include "tagreadermessages.pb.h" +#include "core/messagehandler.h" +#include "core/workerpool.h" +#include + +class QLocalServer; class QProcess; class TagReaderClient : public QObject { @@ -33,6 +37,8 @@ public: typedef AbstractMessageHandler HandlerType; typedef typename HandlerType::ReplyType ReplyType; + static const char* kWorkerExecutableName; + void Start(); ReplyType* ReadFile(const QString& filename); @@ -41,8 +47,11 @@ public: ReplyType* LoadEmbeddedArt(const QString& filename); private: - QProcess* process_; - HandlerType* handler_; + void SendOrQueue(const pb::tagreader::Message& message); + +private: + WorkerPool* worker_pool_; + QList message_queue_; }; typedef TagReaderClient::ReplyType TagReaderReply; diff --git a/src/main.cpp b/src/main.cpp index f08565b8f..2e3229748 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -35,6 +35,7 @@ #include "core/player.h" #include "core/potranslator.h" #include "core/song.h" +#include "core/tagreaderclient.h" #include "core/taskmanager.h" #include "core/ubuntuunityhack.h" #include "core/utilities.h" @@ -407,6 +408,9 @@ int main(int argc, char *argv[]) { GlobalSearchService global_search_service(&global_search); #endif + // Tag reader client + TagReaderClient tag_reader_client; + // Window MainWindow w( database.get(), @@ -418,7 +422,8 @@ int main(int argc, char *argv[]) { &osd, &art_loader, &cover_providers, - &global_search); + &global_search, + &tag_reader_client); #ifdef HAVE_DBUS QObject::connect(&mpris, SIGNAL(RaiseMainWindow()), &w, SLOT(Raise())); #endif diff --git a/src/ui/mainwindow.cpp b/src/ui/mainwindow.cpp index c55a6197c..817849e10 100644 --- a/src/ui/mainwindow.cpp +++ b/src/ui/mainwindow.cpp @@ -161,6 +161,7 @@ MainWindow::MainWindow( ArtLoader* art_loader, CoverProviders* cover_providers, GlobalSearch* global_search, + TagReader* tag_reader_client, QWidget* parent) : QMainWindow(parent), ui_(new Ui_MainWindow), @@ -177,6 +178,7 @@ MainWindow::MainWindow( library_(NULL), global_shortcuts_(new GlobalShortcuts(this)), global_search_(global_search), + tag_reader_client_(tag_reader_client), remote_(NULL), devices_(NULL), library_view_(new LibraryViewContainer(this)), diff --git a/src/ui/mainwindow.h b/src/ui/mainwindow.h index dcb49ced2..89fcab4d8 100644 --- a/src/ui/mainwindow.h +++ b/src/ui/mainwindow.h @@ -68,6 +68,7 @@ class SongInfoBase; class SongInfoView; class SystemTrayIcon; class TagFetcher; +class TagReaderClient; class TaskManager; class TrackSelectionDialog; class TranscodeDialog; @@ -92,6 +93,7 @@ class MainWindow : public QMainWindow, public PlatformInterface { ArtLoader* art_loader, CoverProviders* cover_providers, GlobalSearch* global_search, + TagReaderClient* tag_reader_client, QWidget *parent = 0); ~MainWindow(); @@ -279,6 +281,7 @@ class MainWindow : public QMainWindow, public PlatformInterface { Library* library_; GlobalShortcuts* global_shortcuts_; GlobalSearch* global_search_; + TagReaderClient* tag_reader_client_; Remote* remote_; DeviceManager* devices_; diff --git a/tagreader/common/CMakeLists.txt b/tagreader/common/CMakeLists.txt deleted file mode 100644 index a6066af3c..000000000 --- a/tagreader/common/CMakeLists.txt +++ /dev/null @@ -1,41 +0,0 @@ -include_directories(${PROTOBUF_INCLUDE_DIRS}) -include_directories(${CMAKE_CURRENT_BINARY_DIR}) -include_directories(${CMAKE_SOURCE_DIR}/src) - -set(COMMON_SOURCES - messagehandler.cpp - - ${CMAKE_SOURCE_DIR}/src/core/encoding.cpp - ${CMAKE_SOURCE_DIR}/src/core/logging.cpp -) - -set(COMMON_HEADERS - messagehandler.h -) - -set(COMMON_MESSAGES - tagreadermessages.proto -) - -qt4_wrap_cpp(COMMON_MOC ${COMMON_HEADERS}) -protobuf_generate_cpp(PROTO_SOURCES PROTO_HEADERS ${COMMON_MESSAGES}) - -add_library(clementine-tagreader-common STATIC - ${COMMON_SOURCES} - ${COMMON_MOC} - ${PROTO_SOURCES} -) - -# Use protobuf-lite if it's available -if(PROTOBUF_LITE_LIBRARY AND USE_PROTOBUF_LITE) - set(protobuf ${PROTOBUF_LITE_LIBRARY}) -else(PROTOBUF_LITE_LIBRARY AND USE_PROTOBUF_LITE) - set(protobuf ${PROTOBUF_LIBRARY}) -endif(PROTOBUF_LITE_LIBRARY AND USE_PROTOBUF_LITE) - -target_link_libraries(clementine-tagreader-common - ${protobuf} - ${CMAKE_THREAD_LIBS_INIT} - chardet -) -