summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAndrew Christian <andrew.christian@nokia.com>2012-03-23 07:37:48 -0400
committerChris Craig <ext-chris.craig@nokia.com>2012-03-26 16:44:30 +0200
commita32667270a66fbf1249339c5f1884f8f65f18cef (patch)
treea317203029bdc89bdf43f9285bb637bc405e26d4
parent501c3dd8feaf738ade11ad825a8ac80c64fb19a2 (diff)
Update remote processes to support idle and memory restricted
* Pass Idle CPU requests over remote procotol * Pass Idle CPU grants over remote protocol * Pass Memory Restricted messages over remote protocol * Pass list of internal processes over remote protocol * Update test cases for the above for PipeLauncher and SocketLauncher. * Verify that a PipeLauncher or SocketLauncher can support prelaunched processes correctly. * Added appropriate schema for remote protocol including Halt: Stop the remote client IdleCpuAvailable: grant idle cpu to a remote factory Memory: configure memoryRestricted property IdleCpuRequest: Event from remote process to request idle cpu InternalProcesses: Event from remote process listing all internal processes. * Updated documentation with more accurate pictures of ProcessBackendFactory and ProcessBackendManager hierarchies. Change-Id: I34905049844b80e4018042d67a1a5ed3137fa29a Reviewed-by: Chris Craig <ext-chris.craig@nokia.com>
-rw-r--r--doc/images/processbackendfactory_hierarchy.pngbin10240 -> 9562 bytes
-rw-r--r--doc/images/processbackendmanager_hierarchy.pngbin0 -> 7665 bytes
-rw-r--r--doc/src/basicpm.qdoc27
-rw-r--r--schema/remote/inbound/halt.json7
-rw-r--r--schema/remote/inbound/idlecpuavailable.json7
-rw-r--r--schema/remote/inbound/memory.json8
-rw-r--r--schema/remote/outbound/idlecpurequest.json8
-rw-r--r--schema/remote/outbound/internalprocesses.json16
-rw-r--r--src/core/core-lib.pri1
-rw-r--r--src/core/forklauncher.cpp2
-rw-r--r--src/core/pipelauncher.cpp57
-rw-r--r--src/core/pipelauncher.h4
-rw-r--r--src/core/pipeprocessbackendfactory.cpp45
-rw-r--r--src/core/pipeprocessbackendfactory.h2
-rw-r--r--src/core/preforkprocessbackendfactory.cpp27
-rw-r--r--src/core/preforkprocessbackendfactory.h3
-rw-r--r--src/core/prelaunchprocessbackendfactory.cpp63
-rw-r--r--src/core/prelaunchprocessbackendfactory.h6
-rw-r--r--src/core/processbackendfactory.cpp35
-rw-r--r--src/core/processbackendfactory.h16
-rw-r--r--src/core/processbackendmanager.cpp47
-rw-r--r--src/core/processbackendmanager.h9
-rw-r--r--src/core/processlist.h81
-rw-r--r--src/core/processmanager.cpp2
-rw-r--r--src/core/processmanager.h3
-rw-r--r--src/core/remoteprocessbackendfactory.cpp68
-rw-r--r--src/core/remoteprocessbackendfactory.h10
-rw-r--r--src/core/remoteprotocol.h8
-rw-r--r--src/core/socketlauncher.cpp84
-rw-r--r--src/core/socketlauncher.h7
-rw-r--r--src/core/socketprocessbackendfactory.cpp10
-rw-r--r--src/core/socketprocessbackendfactory.h1
-rw-r--r--tests/auto/processmanager/testPipeLauncher/testPipeLauncher.cpp53
-rw-r--r--tests/auto/processmanager/testSocketLauncher/testSocketLauncher.cpp31
-rw-r--r--tests/auto/processmanager/tst_processmanager.cpp338
35 files changed, 942 insertions, 144 deletions
diff --git a/doc/images/processbackendfactory_hierarchy.png b/doc/images/processbackendfactory_hierarchy.png
index 95b6ac6..52be0a9 100644
--- a/doc/images/processbackendfactory_hierarchy.png
+++ b/doc/images/processbackendfactory_hierarchy.png
Binary files differ
diff --git a/doc/images/processbackendmanager_hierarchy.png b/doc/images/processbackendmanager_hierarchy.png
new file mode 100644
index 0000000..722f5f9
--- /dev/null
+++ b/doc/images/processbackendmanager_hierarchy.png
Binary files differ
diff --git a/doc/src/basicpm.qdoc b/doc/src/basicpm.qdoc
index b5c8d7a..5ae2c8f 100644
--- a/doc/src/basicpm.qdoc
+++ b/doc/src/basicpm.qdoc
@@ -110,7 +110,7 @@ StandardProcessBackendFactory will match anything, so it must go last.
\section1 Inheritence Hierarchy
\image processbackend_hierarchy.png {Process Backend Hierarchy}
-\caption \e{Process Backend Hierarchy}
+\caption \e{ProcessBackend Inheritence Hierarchy}
The virtual ProcessBackend object hierarchy is divided into two
sections: the UnixProcessBackend objects contain a QProcess internally
@@ -120,7 +120,7 @@ launch its own processes, but this mechanism allows the process
manager to not run with setuid privileges.
\image processbackendfactory_hierarchy.png
-\caption \e{Process Backend Factory Hierarchy}
+\caption \e{ProcessBackendFactory Inheritence Hierarchy}
The ProcessBackendFactory hierarchy closely matches the ProcessBackend
hierarchy. The standard and prelaunch subclasses create standard and
@@ -128,4 +128,27 @@ prelaunch process backend objects. The remote subclass is divided
by how it connects to the remote "launcher" program; either over a
pipe connection or a socket connection.
+\image processbackendmanager_hierarchy.png
+\caption \e{ProcessBackendManager Inheritence Hierarchy}
+
+The ProcessBackendManager hierachy is relatively simple. The standard
+ProcessBackendManager is designed to be embedded in an application and
+take commands directly from the application or from a ProcessManager.
+
+The PipeLauncher is a subclass that takes JSON-formatted commands over
+a pipe connection. It is designed to be embedded into a separate
+application that is launched from your main process, and then
+connected to via a PipeProcessBackendFactory or a
+PreforkProcessBackendFactory (depending on how it is launched).
+
+The SocketLauncher is a subclass that takes JSON-formatted commands
+over Unix local socket connections. It is designed to be used in a
+separate application that may be started at any time in the system
+launch (in many cases, by \c{upstart} or \c{/etc/init.d} scripts. It
+uses the \l{JsonStream::JsonServer} class to accept socket
+connections. You connect to a SocketLauncher application using the
+SocketProcessBackendFactory object.
+
+
+
*/
diff --git a/schema/remote/inbound/halt.json b/schema/remote/inbound/halt.json
new file mode 100644
index 0000000..77e43d5
--- /dev/null
+++ b/schema/remote/inbound/halt.json
@@ -0,0 +1,7 @@
+{
+ "title": "Halt schema",
+ "description": "Tell the remote factory to halt operations",
+ "properties": {
+ "remote": { "type": "string", "pattern": "halt", "required": true }
+ }
+}
diff --git a/schema/remote/inbound/idlecpuavailable.json b/schema/remote/inbound/idlecpuavailable.json
new file mode 100644
index 0000000..d22dc53
--- /dev/null
+++ b/schema/remote/inbound/idlecpuavailable.json
@@ -0,0 +1,7 @@
+{
+ "title": "Idle CPU available schema",
+ "description": "Tell the remote factory that an idle request is granted",
+ "properties": {
+ "remote": { "type": "string", "pattern": "idlecpuavailable", "required": true }
+ }
+}
diff --git a/schema/remote/inbound/memory.json b/schema/remote/inbound/memory.json
new file mode 100644
index 0000000..2735f7a
--- /dev/null
+++ b/schema/remote/inbound/memory.json
@@ -0,0 +1,8 @@
+{
+ "title": "Memory schema",
+ "description": "Tell the remote factory that memory is restricted",
+ "properties": {
+ "remote": { "type": "string", "pattern": "memory", "required": true },
+ "restricted": { "type": "boolean", "required": true }
+ }
+}
diff --git a/schema/remote/outbound/idlecpurequest.json b/schema/remote/outbound/idlecpurequest.json
new file mode 100644
index 0000000..fcddfa3
--- /dev/null
+++ b/schema/remote/outbound/idlecpurequest.json
@@ -0,0 +1,8 @@
+{
+ "title": "Idle CPU request schema",
+ "description": "Tell the controller that idle cpu is requested",
+ "properties": {
+ "remote": { "type": "string", "pattern": "idlecpurequested", "required": true },
+ "request": { "type": "boolean", "required": true }
+ }
+}
diff --git a/schema/remote/outbound/internalprocesses.json b/schema/remote/outbound/internalprocesses.json
new file mode 100644
index 0000000..3ed78b2
--- /dev/null
+++ b/schema/remote/outbound/internalprocesses.json
@@ -0,0 +1,16 @@
+{
+ "title": "Internal processes schema",
+ "description": "Give the controller a list of internal processes",
+ "properties": {
+ "remote": { "type": "string", "pattern": "internalprocesses", "required": true },
+ "processes": {
+ "type": "array",
+ "items": {
+ "type": "integer"
+ },
+ "additionalItems": false,
+ "uniqueItems": true,
+ "required": true
+ }
+ }
+}
diff --git a/src/core/core-lib.pri b/src/core/core-lib.pri
index 358192d..7c3f0fe 100644
--- a/src/core/core-lib.pri
+++ b/src/core/core-lib.pri
@@ -5,6 +5,7 @@ INCLUDEPATH += $$PWD
PUBLIC_HEADERS += \
$$PWD/process.h \
+ $$PWD/processlist.h \
$$PWD/processfrontend.h \
$$PWD/processbackend.h \
$$PWD/processbackendfactory.h \
diff --git a/src/core/forklauncher.cpp b/src/core/forklauncher.cpp
index 08efc4b..49ba3c0 100644
--- a/src/core/forklauncher.cpp
+++ b/src/core/forklauncher.cpp
@@ -553,7 +553,7 @@ bool ParentProcess::processFdSet(fd_set& rfds, fd_set& wfds)
// Return 'true' if this is a child process
bool ParentProcess::handleMessage(QJsonObject& message)
{
- if (message.value(RemoteProtocol::remote()).toString() == RemoteProtocol::stop()) {
+ if (message.value(RemoteProtocol::remote()).toString() == RemoteProtocol::halt()) {
// Force all children to stop
foreach (ChildProcess *child, m_children)
child->stop(0);
diff --git a/src/core/pipelauncher.cpp b/src/core/pipelauncher.cpp
index ef70185..d59168c 100644
--- a/src/core/pipelauncher.cpp
+++ b/src/core/pipelauncher.cpp
@@ -53,6 +53,10 @@ QT_BEGIN_NAMESPACE_PROCESSMANAGER
/*!
\class PipeLauncher
\brief The PipeLauncher class accepts input from STDIN and writes data to STDOUT
+
+ The PipeLauncher class is a ProcessBackendManager controlled over Unix
+ pipes. It accepts JSON-formatted commands over STDIN and returns
+ JSON-formatted messages over STDOUT.
*/
/*!
@@ -73,6 +77,47 @@ PipeLauncher::PipeLauncher(QObject *parent)
m_pipe, SLOT(send(const QJsonObject&)));
m_pipe->setFds(STDIN_FILENO, STDOUT_FILENO);
+
+ // Clear the idle delegate - we'll get this from the master
+ setIdleDelegate(0);
+}
+
+/*!
+ \internal
+
+ We override this function to send our idle cpu request back to the
+ originator. The \a request variable will be \c{true} if Idle CPU events are needed.
+ We only forward the request if we don't have an idle delegate.
+ */
+
+void PipeLauncher::handleIdleCpuRequest()
+{
+ Q_ASSERT(m_client);
+
+ if (!idleDelegate()) {
+ QJsonObject object;
+ object.insert(RemoteProtocol::remote(), RemoteProtocol::idlecpurequested());
+ object.insert(RemoteProtocol::request(), idleCpuRequest());
+ m_client->send(object);
+ }
+}
+
+/*!
+ \internal
+
+ We override this function to send our updated list of internal
+ processes back. Please note that we do not include our own process -
+ if that should be on the list, it's up to the controlling side to add it.
+*/
+
+void PipeLauncher::handleInternalProcessChange()
+{
+ Q_ASSERT(m_client);
+
+ QJsonObject object;
+ object.insert(RemoteProtocol::remote(), RemoteProtocol::internalprocesses());
+ object.insert(RemoteProtocol::processes(), pidListToArray(internalProcesses()));
+ m_client->send(object);
}
/*!
@@ -81,9 +126,17 @@ PipeLauncher::PipeLauncher(QObject *parent)
void PipeLauncher::receive(const QJsonObject& message)
{
- if (message.value(RemoteProtocol::remote()).toString() == RemoteProtocol::stop())
+ QString remote = message.value(RemoteProtocol::remote()).toString();
+ if (remote == RemoteProtocol::halt()) // ### TODO: Should halt children
exit(0);
- m_client->receive(message);
+ else if ( remote == RemoteProtocol::memory() )
+ setMemoryRestricted(message.value(RemoteProtocol::restricted()).toBool());
+ else if ( remote == RemoteProtocol::idlecpuavailable() ) {
+ if (!idleDelegate())
+ idleCpuAvailable(); // Only accept idlecpuavailable if we have no idleDelegate
+ }
+ else
+ m_client->receive(message);
}
#include "moc_pipelauncher.cpp"
diff --git a/src/core/pipelauncher.h b/src/core/pipelauncher.h
index 9a43063..afa80e2 100644
--- a/src/core/pipelauncher.h
+++ b/src/core/pipelauncher.h
@@ -56,6 +56,10 @@ class Q_ADDON_PROCESSMANAGER_EXPORT PipeLauncher : public ProcessBackendManager
public:
PipeLauncher(QObject *parent=0);
+protected:
+ virtual void handleIdleCpuRequest();
+ virtual void handleInternalProcessChange();
+
private slots:
void receive(const QJsonObject& object);
diff --git a/src/core/pipeprocessbackendfactory.cpp b/src/core/pipeprocessbackendfactory.cpp
index c9a3435..4b7e946 100644
--- a/src/core/pipeprocessbackendfactory.cpp
+++ b/src/core/pipeprocessbackendfactory.cpp
@@ -99,7 +99,7 @@ void PipeProcessBackendFactory::stopRemoteProcess()
{
if (m_process && m_process->state() == QProcess::Running) {
QJsonObject object;
- object.insert(RemoteProtocol::remote(), RemoteProtocol::stop());
+ object.insert(RemoteProtocol::remote(), RemoteProtocol::halt());
m_process->write(QJsonDocument(object).toBinaryData());
m_process->waitForBytesWritten(); // Block until they have been written
m_process = NULL;
@@ -123,18 +123,6 @@ bool PipeProcessBackendFactory::canCreate(const ProcessInfo &info) const
}
/*!
- If there is a pipe process running, it will be returned here.
- */
-
-QList<Q_PID> PipeProcessBackendFactory::internalProcesses()
-{
- QList<Q_PID> list;
- if (m_process && m_process->state() == QProcess::Running)
- list << m_process->pid();
- return list;
-}
-
-/*!
Sets the ProcessInfo that is used to create the pipe process to \a processInfo.
An internal copy is made of the \a processInfo object.
This routine will start the pipe process.
@@ -204,19 +192,25 @@ ProcessInfo *PipeProcessBackendFactory::processInfo() const
}
/*!
+ Return the local process if it is running
+ */
+
+PidList PipeProcessBackendFactory::localInternalProcesses() const
+{
+ PidList result;
+ if (m_process && m_process->state() == QProcess::Running)
+ result << m_process->pid();
+ return result;
+}
+
+/*!
Send \a message to a pipe process.
*/
bool PipeProcessBackendFactory::send(const QJsonObject& message)
{
- if (m_process->state() != QProcess::Running) {
- qCritical("Pipe process not running");
- return false;
- }
- if (m_process->write(QJsonDocument(message).toBinaryData()) == -1) {
- qCritical("Unable to write to pipe process");
- return false;
- }
- return true;
+ // qDebug() << Q_FUNC_INFO << message;
+ return (m_process->state() == QProcess::Running &&
+ m_process->write(QJsonDocument(message).toBinaryData()) != -1);
}
@@ -247,6 +241,7 @@ void PipeProcessBackendFactory::pipeReadyReadStandardError()
void PipeProcessBackendFactory::pipeStarted()
{
+ handleConnected();
}
void PipeProcessBackendFactory::pipeError(QProcess::ProcessError error)
@@ -261,9 +256,11 @@ void PipeProcessBackendFactory::pipeFinished(int exitCode, QProcess::ExitStatus
m_process = NULL;
}
-void PipeProcessBackendFactory::pipeStateChanged(QProcess::ProcessState state)
+void PipeProcessBackendFactory::pipeStateChanged(QProcess::ProcessState)
{
- Q_UNUSED(state);
+ // This may result in a small amount of extra because not all transitions
+ // result in a change in the number of internal processes.
+ setInternalProcesses(localInternalProcesses());
}
diff --git a/src/core/pipeprocessbackendfactory.h b/src/core/pipeprocessbackendfactory.h
index 7dbee9e..ed76fd6 100644
--- a/src/core/pipeprocessbackendfactory.h
+++ b/src/core/pipeprocessbackendfactory.h
@@ -54,7 +54,6 @@ public:
virtual ~PipeProcessBackendFactory();
virtual bool canCreate(const ProcessInfo &info) const;
- virtual QList<Q_PID> internalProcesses();
ProcessInfo *processInfo() const;
void setProcessInfo(ProcessInfo *processInfo);
@@ -64,6 +63,7 @@ signals:
void processInfoChanged();
protected:
+ virtual PidList localInternalProcesses() const;
virtual bool send(const QJsonObject&);
private slots:
diff --git a/src/core/preforkprocessbackendfactory.cpp b/src/core/preforkprocessbackendfactory.cpp
index 7b8eca6..d9bbba4 100644
--- a/src/core/preforkprocessbackendfactory.cpp
+++ b/src/core/preforkprocessbackendfactory.cpp
@@ -82,6 +82,7 @@ PreforkProcessBackendFactory::PreforkProcessBackendFactory(QObject *parent)
m_pipe = new QtAddOn::JsonStream::JsonPipe(this);
connect(m_pipe, SIGNAL(messageReceived(const QJsonObject&)),
SLOT(receive(const QJsonObject&)));
+ handleConnected();
}
/*!
@@ -93,7 +94,7 @@ PreforkProcessBackendFactory::~PreforkProcessBackendFactory()
const PreforkChildData *data = Prefork::instance()->at(m_index);
if (data) {
QJsonObject message;
- message.insert(RemoteProtocol::remote(), RemoteProtocol::stop());
+ message.insert(RemoteProtocol::remote(), RemoteProtocol::halt());
m_pipe->send(message);
m_pipe->waitForBytesWritten();
}
@@ -128,25 +129,29 @@ void PreforkProcessBackendFactory::setIndex(int index)
}
/*!
- Send a \a message to the preforked process
+ Return the local process
*/
-bool PreforkProcessBackendFactory::send(const QJsonObject& message)
+PidList PreforkProcessBackendFactory::localInternalProcesses() const
{
- return m_pipe->send(message);
+ Prefork *prefork = Prefork::instance();
+ Q_ASSERT(prefork);
+
+ QList<Q_PID> list;
+ if (m_index >= 0 && m_index < prefork->size()) {
+ const PreforkChildData *data = prefork->at(m_index);
+ list << data->pid;
+ }
+ return list;
}
/*!
- Return the preforked process
+ Send a \a message to the preforked process
*/
-QList<Q_PID> PreforkProcessBackendFactory::internalProcesses()
+bool PreforkProcessBackendFactory::send(const QJsonObject& message)
{
- QList<Q_PID> list;
- const PreforkChildData *data = Prefork::instance()->at(m_index);
- if (data)
- list << data->pid;
- return list;
+ return m_pipe->send(message);
}
/*!
diff --git a/src/core/preforkprocessbackendfactory.h b/src/core/preforkprocessbackendfactory.h
index 912668c..b7e79ba 100644
--- a/src/core/preforkprocessbackendfactory.h
+++ b/src/core/preforkprocessbackendfactory.h
@@ -57,12 +57,11 @@ public:
int index() const;
void setIndex(int index);
- virtual QList<Q_PID> internalProcesses();
-
signals:
void indexChanged();
protected:
+ virtual PidList localInternalProcesses() const;
virtual bool send(const QJsonObject&);
private:
diff --git a/src/core/prelaunchprocessbackendfactory.cpp b/src/core/prelaunchprocessbackendfactory.cpp
index eb64464..3d839c7 100644
--- a/src/core/prelaunchprocessbackendfactory.cpp
+++ b/src/core/prelaunchprocessbackendfactory.cpp
@@ -111,16 +111,15 @@ bool PrelaunchProcessBackendFactory::canCreate(const ProcessInfo &info) const
ProcessBackend * PrelaunchProcessBackendFactory::create(const ProcessInfo &info, QObject *parent)
{
Q_ASSERT(m_info);
-
PrelaunchProcessBackend *prelaunch = m_prelaunch;
if (hasPrelaunchedProcess()) {
// qDebug() << "Using existing prelaunch";
m_prelaunch = NULL;
- startPrelaunchTimer();
prelaunch->setInfo(info);
prelaunch->setParent(parent);
prelaunch->disconnect(this);
+ updateState();
} else {
// qDebug() << "Creating prelaunch from scratch";
prelaunch = new PrelaunchProcessBackend(*m_info, parent);
@@ -131,18 +130,6 @@ ProcessBackend * PrelaunchProcessBackendFactory::create(const ProcessInfo &info,
}
/*!
- If there is a prelaunched process running, it will be return here.
- */
-
-QList<Q_PID> PrelaunchProcessBackendFactory::internalProcesses()
-{
- QList<Q_PID> list;
- if (m_prelaunch && m_prelaunch->isReady())
- list << m_prelaunch->pid();
- return list;
-}
-
-/*!
Return the prelaunched process information
*/
@@ -164,15 +151,12 @@ void PrelaunchProcessBackendFactory::setPrelaunchEnabled(bool value)
if (m_prelaunchEnabled != value) {
m_prelaunchEnabled = value;
if (!m_prelaunchEnabled) {
- setIdleCpuRequest(false);
if (m_prelaunch) {
m_prelaunch->deleteLater();
m_prelaunch = NULL;
}
- } else {
- Q_ASSERT(m_prelaunch == NULL);
- startPrelaunchTimer();
}
+ updateState();
emit prelaunchEnabledChanged();
}
}
@@ -191,15 +175,12 @@ bool PrelaunchProcessBackendFactory::hasPrelaunchedProcess() const
void PrelaunchProcessBackendFactory::handleMemoryRestrictionChange()
{
if (m_memoryRestricted) {
- setIdleCpuRequest(false);
if (m_prelaunch) {
delete m_prelaunch; // This will kill the child process as well
m_prelaunch = NULL;
}
- } else {
- Q_ASSERT(m_prelaunch == NULL);
- startPrelaunchTimer();
}
+ updateState();
}
/*!
@@ -216,17 +197,20 @@ PrelaunchProcessBackend *PrelaunchProcessBackendFactory::prelaunchProcessBackend
void PrelaunchProcessBackendFactory::idleCpuAvailable()
{
- if (!m_prelaunch && !m_memoryRestricted && m_info) {
- setIdleCpuRequest(false); // Might delay this until the prelaunch is done....
-
+ // qDebug() << Q_FUNC_INFO;
+ if (m_prelaunchEnabled && !m_prelaunch && !m_memoryRestricted && m_info) {
+ // qDebug() << Q_FUNC_INFO << "...launching";
m_prelaunch = new PrelaunchProcessBackend(*m_info, this);
connect(m_prelaunch, SIGNAL(finished(int,QProcess::ExitStatus)),
SLOT(prelaunchFinished(int,QProcess::ExitStatus)));
connect(m_prelaunch, SIGNAL(error(QProcess::ProcessError)),
SLOT(prelaunchError(QProcess::ProcessError)));
+ connect(m_prelaunch, SIGNAL(stateChanged(QProcess::ProcessState)),
+ SLOT(updateState()));
m_prelaunch->prestart();
emit processPrelaunched();
}
+ updateState();
}
/*!
@@ -241,7 +225,7 @@ void PrelaunchProcessBackendFactory::prelaunchFinished(int exitCode, QProcess::E
m_prelaunch->deleteLater();
m_prelaunch = NULL;
}
- startPrelaunchTimer();
+ updateState();
}
/*!
@@ -259,21 +243,26 @@ void PrelaunchProcessBackendFactory::prelaunchError(QProcess::ProcessError err)
if (err == QProcess::FailedToStart) {
qWarning() << Q_FUNC_INFO << "disabling prelaunch because of process errors";
m_prelaunchEnabled = false;
-
- }
- else {
- // ### TODO: This isn't optimal
- startPrelaunchTimer();
}
+
+ updateState();
}
/*!
- Starts the prelaunch timer only if prelaunching is enabled.
+ Update our presented state, consisting of whether or not we need
+ idle CPU resources and how many internal processes we are running.
*/
-void PrelaunchProcessBackendFactory::startPrelaunchTimer()
+void PrelaunchProcessBackendFactory::updateState()
{
- if (m_prelaunchEnabled)
- setIdleCpuRequest(true);
+ Q_ASSERT(!m_prelaunch || m_prelaunchEnabled); // If prelaunch is not enabled, we must not have a prelaunch process
+ Q_ASSERT(!m_prelaunch || !m_memoryRestricted); // If memory is restricted, we must not have a prelaunch process
+
+ setIdleCpuRequest(!m_memoryRestricted && m_prelaunchEnabled && !m_prelaunch && m_info);
+
+ PidList list;
+ if (m_prelaunch && m_prelaunch->isReady())
+ list << m_prelaunch->pid();
+ setInternalProcesses(list);
}
/*!
@@ -295,10 +284,8 @@ void PrelaunchProcessBackendFactory::setProcessInfo(ProcessInfo *processInfo)
m_prelaunch->deleteLater();
m_prelaunch = NULL;
}
- startPrelaunchTimer();
- } else {
- setIdleCpuRequest(false);
}
+ updateState();
emit processInfoChanged();
}
}
diff --git a/src/core/prelaunchprocessbackendfactory.h b/src/core/prelaunchprocessbackendfactory.h
index 4a5c0b5..c2b9543 100644
--- a/src/core/prelaunchprocessbackendfactory.h
+++ b/src/core/prelaunchprocessbackendfactory.h
@@ -61,8 +61,6 @@ public:
virtual bool canCreate(const ProcessInfo &info) const;
virtual ProcessBackend *create(const ProcessInfo& info, QObject *parent);
- virtual QList<Q_PID> internalProcesses();
-
ProcessInfo *processInfo() const;
void setProcessInfo(ProcessInfo *processInfo);
void setProcessInfo(ProcessInfo& processInfo);
@@ -87,9 +85,7 @@ protected slots:
private slots:
void prelaunchFinished(int, QProcess::ExitStatus);
void prelaunchError(QProcess::ProcessError);
-
-private:
- void startPrelaunchTimer();
+ void updateState();
private:
PrelaunchProcessBackend *m_prelaunch;
diff --git a/src/core/processbackendfactory.cpp b/src/core/processbackendfactory.cpp
index 17be8d5..004a6dd 100644
--- a/src/core/processbackendfactory.cpp
+++ b/src/core/processbackendfactory.cpp
@@ -41,8 +41,6 @@
#include "matchdelegate.h"
#include "rewritedelegate.h"
-#include <QDebug>
-
QT_BEGIN_NAMESPACE_PROCESSMANAGER
/*!
@@ -53,6 +51,11 @@ QT_BEGIN_NAMESPACE_PROCESSMANAGER
*/
/*!
+ \property ProcessBackendFactory::internalProcesses
+ \brief A list of the internal processes.
+*/
+
+/*!
\property ProcessBackendFactory::matchDelegate
\brief A MatchDelegate object assigned to this factory.
*/
@@ -102,14 +105,25 @@ void ProcessBackendFactory::setMemoryRestricted(bool memoryRestricted)
}
/*!
- Override this is subclasses to return a list of internal
- processes that are contained in this factory. The default
- class returns an empty list.
+ Return a list of internal processes that are in use by this factory
*/
-QList<Q_PID> ProcessBackendFactory::internalProcesses()
+PidList ProcessBackendFactory::internalProcesses() const
{
- return QList<Q_PID>();
+ return m_internalProcesses;
+}
+
+/*!
+ Set the list of internal processes. This should be a sorted
+ list. The \a plist argument is a list of processes.
+ */
+
+void ProcessBackendFactory::setInternalProcesses(const PidList& plist)
+{
+ if (!compareSortedLists(plist, m_internalProcesses)) {
+ m_internalProcesses = plist;
+ emit internalProcessesChanged();
+ }
}
/*!
@@ -200,7 +214,6 @@ void ProcessBackendFactory::setIdleCpuRequest(bool value)
void ProcessBackendFactory::idleCpuAvailable()
{
- qDebug() << Q_FUNC_INFO;
}
/*!
@@ -237,6 +250,12 @@ void ProcessBackendFactory::rewrite(ProcessInfo& info)
}
/*!
+ \fn void ProcessBackendFactory::internalProcessesChanged()
+
+ Signal emitted whenever the list of internal processes has changed.
+*/
+
+/*!
\fn void ProcessBackendFactory::matchDelegateChanged()
Signal emitted whenever the MatchDelegate is changed.
diff --git a/src/core/processbackendfactory.h b/src/core/processbackendfactory.h
index ed17d61..0759f90 100644
--- a/src/core/processbackendfactory.h
+++ b/src/core/processbackendfactory.h
@@ -44,6 +44,7 @@
#include <QProcessEnvironment>
#include "processmanager-global.h"
+#include "processlist.h"
QT_BEGIN_NAMESPACE_PROCESSMANAGER
@@ -55,8 +56,9 @@ class RewriteDelegate;
class Q_ADDON_PROCESSMANAGER_EXPORT ProcessBackendFactory : public QObject
{
Q_OBJECT
- Q_PROPERTY(MatchDelegate* matchDelegate READ matchDelegate WRITE setMatchDelegate NOTIFY matchDelegateChanged);
- Q_PROPERTY(RewriteDelegate* rewriteDelegate READ rewriteDelegate WRITE setRewriteDelegate NOTIFY rewriteDelegateChanged);
+ Q_PROPERTY(PidList internalProcesses READ internalProcesses NOTIFY internalProcessesChanged)
+ Q_PROPERTY(MatchDelegate* matchDelegate READ matchDelegate WRITE setMatchDelegate NOTIFY matchDelegateChanged)
+ Q_PROPERTY(RewriteDelegate* rewriteDelegate READ rewriteDelegate WRITE setRewriteDelegate NOTIFY rewriteDelegateChanged)
Q_PROPERTY(bool idleCpuRequest READ idleCpuRequest NOTIFY idleCpuRequestChanged)
public:
@@ -66,8 +68,9 @@ public:
virtual void rewrite(ProcessInfo& info);
virtual ProcessBackend *create(const ProcessInfo& info, QObject *parent) = 0;
- void setMemoryRestricted(bool);
- virtual QList<Q_PID> internalProcesses();
+ void setMemoryRestricted(bool);
+
+ PidList internalProcesses() const;
MatchDelegate * matchDelegate() const;
void setMatchDelegate(MatchDelegate *);
@@ -79,15 +82,18 @@ public:
virtual void idleCpuAvailable();
signals:
+ void internalProcessesChanged();
void matchDelegateChanged();
void rewriteDelegateChanged();
void idleCpuRequestChanged();
protected:
+ void setIdleCpuRequest(bool);
+ virtual void setInternalProcesses(const PidList&);
virtual void handleMemoryRestrictionChange();
- virtual void setIdleCpuRequest(bool);
protected:
+ PidList m_internalProcesses;
MatchDelegate *m_matchDelegate;
RewriteDelegate *m_rewriteDelegate;
bool m_memoryRestricted;
diff --git a/src/core/processbackendmanager.cpp b/src/core/processbackendmanager.cpp
index 4a63f1a..2d338c4 100644
--- a/src/core/processbackendmanager.cpp
+++ b/src/core/processbackendmanager.cpp
@@ -37,6 +37,8 @@
**
****************************************************************************/
+#include <QDebug>
+
#include "processbackendmanager.h"
#include "processbackendfactory.h"
#include "processbackend.h"
@@ -165,6 +167,7 @@ void ProcessBackendManager::addFactory(ProcessBackendFactory *factory)
m_factories.append(factory);
factory->setParent(this);
factory->setMemoryRestricted(m_memoryRestricted);
+ connect(factory, SIGNAL(internalProcessesChanged()), SLOT(updateInternalProcesses()));
connect(factory, SIGNAL(idleCpuRequestChanged()), SLOT(updateIdleCpuRequest()));
updateIdleCpuRequest();
}
@@ -173,12 +176,9 @@ void ProcessBackendManager::addFactory(ProcessBackendFactory *factory)
Return a list of all internal processes being used by factories
*/
-QList<Q_PID> ProcessBackendManager::internalProcesses()
+PidList ProcessBackendManager::internalProcesses() const
{
- QList<Q_PID> plist;
- foreach (ProcessBackendFactory *factory, m_factories)
- plist.append(factory->internalProcesses());
- return plist;
+ return m_internalProcesses;
}
/*!
@@ -270,21 +270,50 @@ void ProcessBackendManager::updateIdleCpuRequest()
m_idleCpuRequest = request;
if (m_idleDelegate)
m_idleDelegate->requestIdleCpu(m_idleCpuRequest);
- handleIdleCpuRequest(m_idleCpuRequest);
+ handleIdleCpuRequest();
+ }
+}
+
+/*!
+ Update the list of internal processes
+ */
+
+void ProcessBackendManager::updateInternalProcesses()
+{
+ QList<Q_PID> plist;
+ foreach (ProcessBackendFactory *factory, m_factories)
+ plist.append(factory->internalProcesses());
+ qSort(plist);
+ if (!compareSortedLists(plist, m_internalProcesses)) {
+ m_internalProcesses = plist;
+ handleInternalProcessChange();
+ emit internalProcessesChanged();
}
}
/*!
Override this function to customize your handling of Idle CPU requests.
- The \a request variable will be \c{true} if Idle CPU events are needed.
*/
-void ProcessBackendManager::handleIdleCpuRequest(bool request)
+void ProcessBackendManager::handleIdleCpuRequest()
{
- Q_UNUSED(request);
}
/*!
+ Override this function to customize your handling of changes in the
+ list of internal processes.
+ */
+
+void ProcessBackendManager::handleInternalProcessChange()
+{
+}
+
+/*!
+ \fn void ProcessBackendManager::internalProcessesChanged()
+ Signal emitted whenever the list of internal processes has changed.
+*/
+
+/*!
\fn void ProcessBackendManager::idleDelegateChanged()
Signal emitted whenever the IdleDelegate is changed.
*/
diff --git a/src/core/processbackendmanager.h b/src/core/processbackendmanager.h
index c976cb3..bfb5597 100644
--- a/src/core/processbackendmanager.h
+++ b/src/core/processbackendmanager.h
@@ -45,6 +45,7 @@
#include <QProcessEnvironment>
#include "processmanager-global.h"
+#include "processlist.h"
QT_BEGIN_NAMESPACE_PROCESSMANAGER
@@ -65,7 +66,7 @@ public:
ProcessBackend *create(const ProcessInfo& info, QObject *parent=0);
void addFactory(ProcessBackendFactory *factory);
- QList<Q_PID> internalProcesses();
+ PidList internalProcesses() const;
void setMemoryRestricted(bool);
bool memoryRestricted() const;
@@ -75,19 +76,23 @@ public:
bool idleCpuRequest() const { return m_idleCpuRequest; }
protected:
- virtual void handleIdleCpuRequest(bool request);
+ virtual void handleIdleCpuRequest();
+ virtual void handleInternalProcessChange();
signals:
void idleDelegateChanged();
+ void internalProcessesChanged();
protected slots:
void idleCpuAvailable();
private slots:
void updateIdleCpuRequest();
+ void updateInternalProcesses();
private:
QList<ProcessBackendFactory*> m_factories;
+ PidList m_internalProcesses;
IdleDelegate *m_idleDelegate;
bool m_memoryRestricted;
bool m_idleCpuRequest;
diff --git a/src/core/processlist.h b/src/core/processlist.h
new file mode 100644
index 0000000..c96a618
--- /dev/null
+++ b/src/core/processlist.h
@@ -0,0 +1,81 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef PROCESS_LIST_H
+#define PROCESS_LIST_H
+
+#include <QList>
+#include <QProcess>
+#include <QJsonArray>
+
+#include "processmanager-global.h"
+
+QT_BEGIN_NAMESPACE_PROCESSMANAGER
+
+typedef QList<Q_PID> PidList;
+
+inline bool compareSortedLists(const PidList& a, const PidList& b)
+{
+ if (a.size() != b.size())
+ return false;
+ for (int i=0 ; i < a.size() ; i++)
+ if (a.at(i) != b.at(i))
+ return false;
+ return true;
+}
+
+inline QJsonArray pidListToArray(const PidList& plist)
+{
+ QJsonArray array;
+ foreach (Q_PID pid, plist)
+ array.append((double) pid);
+ return array;
+}
+
+inline PidList arrayToPidList(const QJsonArray& array)
+{
+ PidList plist;
+ for (int i = 0 ; i < array.size() ; i++)
+ plist.append((int) array.at(i).toDouble());
+ return plist;
+}
+
+QT_END_NAMESPACE_PROCESSMANAGER
+
+#endif // PROCESS_LIST_H
diff --git a/src/core/processmanager.cpp b/src/core/processmanager.cpp
index 9bee6aa..32eca25 100644
--- a/src/core/processmanager.cpp
+++ b/src/core/processmanager.cpp
@@ -193,7 +193,7 @@ QStringList ProcessManager::names() const
Return a list of all internal processes being used by factories
*/
-QList<Q_PID> ProcessManager::internalProcesses()
+PidList ProcessManager::internalProcesses() const
{
return m_backend->internalProcesses();
}
diff --git a/src/core/processmanager.h b/src/core/processmanager.h
index 57ef3af..70e8ef9 100644
--- a/src/core/processmanager.h
+++ b/src/core/processmanager.h
@@ -46,6 +46,7 @@
#include "processmanager-global.h"
#include "process.h"
+#include "processlist.h"
QT_BEGIN_NAMESPACE_PROCESSMANAGER
@@ -77,7 +78,7 @@ public:
Q_INVOKABLE int size() const;
Q_INVOKABLE void addBackendFactory(ProcessBackendFactory *factory);
- Q_INVOKABLE QList<Q_PID> internalProcesses();
+ Q_INVOKABLE PidList internalProcesses() const;
void setMemoryRestricted(bool);
bool memoryRestricted() const;
diff --git a/src/core/remoteprocessbackendfactory.cpp b/src/core/remoteprocessbackendfactory.cpp
index 012fc1d..67293ac 100644
--- a/src/core/remoteprocessbackendfactory.cpp
+++ b/src/core/remoteprocessbackendfactory.cpp
@@ -39,6 +39,7 @@
#include "remoteprocessbackendfactory.h"
#include "remoteprocessbackend.h"
+#include "remoteprotocol.h"
QT_BEGIN_NAMESPACE_PROCESSMANAGER
@@ -75,6 +76,9 @@ const int kRemoteTimerInterval = 1000;
\li \c{{ "command": "write", "id": NUM, "data": STRING }}
\li Write a data string to the remote process. We assume that the
data string is a valid local 8 bit string.
+ \row
+ \li \c{{ "command": "memory", "restricted": bool }}
+ \li Let the remote process know if memory use is restricted.
\endtable
The following are events that are sent by the remote process
@@ -138,15 +142,73 @@ ProcessBackend * RemoteProcessBackendFactory::create(const ProcessInfo& info, QO
}
/*!
+ Idle CPU is available. Send the message over the wire
+ */
+
+void RemoteProcessBackendFactory::idleCpuAvailable()
+{
+ QJsonObject object;
+ object.insert(RemoteProtocol::remote(), RemoteProtocol::idlecpuavailable());
+ send(object);
+}
+
+/*!
+ Tell the remote factory that memory is restricted and space should be freed up.
+ */
+
+void RemoteProcessBackendFactory::handleMemoryRestrictionChange()
+{
+ QJsonObject object;
+ object.insert(RemoteProtocol::remote(), RemoteProtocol::memory());
+ object.insert(RemoteProtocol::restricted(), m_memoryRestricted);
+ send(object);
+}
+
+/*!
+ Return a list of local internal processes. This is separate from the
+ remote internal process list, which comes from the remote host.
+ You should override this function is a subclass if your subclass is
+ holding a process object
+*/
+
+PidList RemoteProcessBackendFactory::localInternalProcesses() const
+{
+ return PidList();
+}
+
+/*!
+ Handle first connection to the remote process.
+ This function should be called by subclasses.
+ */
+
+void RemoteProcessBackendFactory::handleConnected()
+{
+ handleMemoryRestrictionChange(); // Sends command="memory" message
+}
+
+/*!
Receive a remote \a message and dispatch it to the correct recipient.
Call this function from your subclass to properly dispatch messages.
*/
void RemoteProcessBackendFactory::receive(const QJsonObject& message)
{
- int id = message.value(QLatin1String("id")).toDouble();
- if (m_backendMap.contains(id))
- m_backendMap.value(id)->receive(message);
+ // qDebug() << Q_FUNC_INFO << message;
+
+ QString remote = message.value(RemoteProtocol::remote()).toString();
+ if (remote == RemoteProtocol::idlecpurequested())
+ setIdleCpuRequest(message.value(RemoteProtocol::request()).toBool());
+ else if (remote == RemoteProtocol::internalprocesses()) {
+ PidList plist = localInternalProcesses() +
+ arrayToPidList(message.value(RemoteProtocol::processes()).toArray());
+ qSort(plist);
+ setInternalProcesses(plist);
+ }
+ else {
+ int id = message.value(QLatin1String("id")).toDouble();
+ if (m_backendMap.contains(id))
+ m_backendMap.value(id)->receive(message);
+ }
}
/*!
diff --git a/src/core/remoteprocessbackendfactory.h b/src/core/remoteprocessbackendfactory.h
index ac2dac4..86417b6 100644
--- a/src/core/remoteprocessbackendfactory.h
+++ b/src/core/remoteprocessbackendfactory.h
@@ -56,10 +56,16 @@ public:
virtual ~RemoteProcessBackendFactory();
virtual ProcessBackend *create(const ProcessInfo& info, QObject *parent);
+ virtual void idleCpuAvailable();
+
+protected slots:
+ void handleConnected();
+ void receive(const QJsonObject&);
protected:
- virtual bool send(const QJsonObject&) = 0;
- Q_INVOKABLE void receive(const QJsonObject&);
+ virtual void handleMemoryRestrictionChange();
+ virtual PidList localInternalProcesses() const;
+ virtual bool send(const QJsonObject&) = 0;
private:
void backendDestroyed(int);
diff --git a/src/core/remoteprotocol.h b/src/core/remoteprotocol.h
index 99f7131..f2b86cf 100644
--- a/src/core/remoteprotocol.h
+++ b/src/core/remoteprotocol.h
@@ -56,14 +56,22 @@ public:
static inline const QString exitCode() { return QStringLiteral("exitCode"); }
static inline const QString exitStatus() { return QStringLiteral("exitStatus"); }
static inline const QString finished() { return QStringLiteral("finished"); }
+ static inline const QString halt() { return QStringLiteral("halt"); }
static inline const QString id() { return QStringLiteral("id"); }
+ static inline const QString idlecpurequested() { return QStringLiteral("idlecpurequested"); }
+ static inline const QString idlecpuavailable() { return QStringLiteral("idlecpuavailable"); }
static inline const QString info() { return QStringLiteral("info"); }
+ static inline const QString internalprocesses() { return QStringLiteral("internalprocesses"); }
static inline const QString key() { return QStringLiteral("key"); }
+ static inline const QString memory() { return QStringLiteral("memory"); }
static inline const QString oomAdjustment() { return QStringLiteral("oomAdjustment"); }
static inline const QString output() { return QStringLiteral("output"); }
static inline const QString pid() { return QStringLiteral("pid"); }
static inline const QString priority() { return QStringLiteral("priority"); }
+ static inline const QString processes() { return QStringLiteral("processes"); }
static inline const QString remote() { return QStringLiteral("remote"); }
+ static inline const QString restricted() { return QStringLiteral("restricted"); }
+ static inline const QString request() { return QStringLiteral("request"); }
static inline const QString set() { return QStringLiteral("set"); }
static inline const QString signal() { return QStringLiteral("signal"); }
static inline const QString start() { return QStringLiteral("start"); }
diff --git a/src/core/socketlauncher.cpp b/src/core/socketlauncher.cpp
index 909dc5f..dd7be2d 100644
--- a/src/core/socketlauncher.cpp
+++ b/src/core/socketlauncher.cpp
@@ -37,10 +37,12 @@
**
****************************************************************************/
+#include <QDebug>
#include <signal.h>
#include "socketlauncher.h"
#include "launcherclient.h"
+#include "remoteprotocol.h"
QT_BEGIN_NAMESPACE_PROCESSMANAGER
@@ -69,6 +71,8 @@ SocketLauncher::SocketLauncher(QObject *parent)
SLOT(connectionAdded(const QString&)));
connect(m_server, SIGNAL(connectionRemoved(const QString&)),
SLOT(connectionRemoved(const QString&)));
+
+ setIdleDelegate(0); // Clear the idle delegate and get this from the master
}
/*!
@@ -109,6 +113,19 @@ void SocketLauncher::connectionAdded(const QString& identifier)
connect(client, SIGNAL(send(const QJsonObject&)), SLOT(send(const QJsonObject&)));
m_idToClient.insert(identifier, client);
m_clientToId.insert(client, identifier);
+
+ // Send our current idle request and internal process list
+ if (!idleDelegate()) {
+ QJsonObject object;
+ object.insert(RemoteProtocol::remote(), RemoteProtocol::idlecpurequested());
+ object.insert(RemoteProtocol::request(), idleCpuRequest());
+ sendToClient(object, client);
+ }
+
+ QJsonObject object;
+ object.insert(RemoteProtocol::remote(), RemoteProtocol::internalprocesses());
+ object.insert(RemoteProtocol::processes(), pidListToArray(internalProcesses()));
+ sendToClient(object, client);
}
/*!
@@ -124,26 +141,85 @@ void SocketLauncher::connectionRemoved(const QString& identifier)
}
/*!
+ \internal
+
+ We override this function to send our idle cpu request back to the
+ originator. This is a little odd, in that we send the idle cpu
+ request back to all connected clients. Hopefully only one will
+ respond.
+
+ We only send the idlecpurequest message if we don't have an idle delegate
+ */
+
+void SocketLauncher::handleIdleCpuRequest()
+{
+ if (!idleDelegate()) {
+ QJsonObject object;
+ object.insert(RemoteProtocol::remote(), RemoteProtocol::idlecpurequested());
+ object.insert(RemoteProtocol::request(), idleCpuRequest());
+ m_server->broadcast(object);
+ }
+}
+
+/*!
+ \internal
+
+ We override this function to send our updated list of internal
+ processes to all clients
+*/
+
+void SocketLauncher::handleInternalProcessChange()
+{
+ QJsonObject object;
+ object.insert(RemoteProtocol::remote(), RemoteProtocol::internalprocesses());
+ object.insert(RemoteProtocol::processes(), pidListToArray(internalProcesses()));
+ m_server->broadcast(object);
+}
+
+/*!
\internal
*/
+
void SocketLauncher::messageReceived(const QString& identifier, const QJsonObject& message)
{
- LauncherClient *client = m_idToClient.value(identifier);
- if (client)
- client->receive(message);
+ QString remote = message.value(RemoteProtocol::remote()).toString();
+ if (remote == RemoteProtocol::halt())
+ qDebug() << Q_FUNC_INFO << "Received halt request; ignoring";
+ else if ( remote == RemoteProtocol::memory() )
+ setMemoryRestricted(message.value(RemoteProtocol::restricted()).toBool());
+ else if ( remote == RemoteProtocol::idlecpuavailable() ) {
+ if (!idleDelegate())
+ idleCpuAvailable();
+ }
+ else {
+ LauncherClient *client = m_idToClient.value(identifier);
+ if (client)
+ client->receive(message);
+ }
}
/*!
\internal
+
+ This function should only be called from a signal raised in LauncherClient
*/
void SocketLauncher::send(const QJsonObject& message)
{
- LauncherClient *client = qobject_cast<LauncherClient *>(sender());
+ sendToClient(message, qobject_cast<LauncherClient *>(sender()));
+}
+
+/*!
+ \internal
+ */
+
+void SocketLauncher::sendToClient(const QJsonObject& message, LauncherClient *client)
+{
Q_ASSERT(client);
Q_ASSERT(m_server);
m_server->send(m_clientToId.value(client), message);
}
+
#include "moc_socketlauncher.cpp"
QT_END_NAMESPACE_PROCESSMANAGER
diff --git a/src/core/socketlauncher.h b/src/core/socketlauncher.h
index ded68cb..004ba77 100644
--- a/src/core/socketlauncher.h
+++ b/src/core/socketlauncher.h
@@ -61,6 +61,10 @@ public:
QtAddOn::JsonStream::JsonServer * server() const;
+protected:
+ virtual void handleIdleCpuRequest();
+ virtual void handleInternalProcessChange();
+
private slots:
void connectionAdded(const QString& identifier);
void connectionRemoved(const QString& identifier);
@@ -68,6 +72,9 @@ private slots:
void send(const QJsonObject& message);
private:
+ void sendToClient(const QJsonObject& message, LauncherClient *client);
+
+private:
QtAddOn::JsonStream::JsonServer *m_server;
QMap<QString, LauncherClient*> m_idToClient;
QMap<LauncherClient*, QString> m_clientToId;
diff --git a/src/core/socketprocessbackendfactory.cpp b/src/core/socketprocessbackendfactory.cpp
index a2f2de2..d30e7fc 100644
--- a/src/core/socketprocessbackendfactory.cpp
+++ b/src/core/socketprocessbackendfactory.cpp
@@ -67,6 +67,7 @@ SocketProcessBackendFactory::SocketProcessBackendFactory(QObject *parent)
: RemoteProcessBackendFactory(parent)
{
m_socket = new QLocalSocket(this);
+ connect(m_socket, SIGNAL(connected()), SLOT(connected()));
connect(m_socket, SIGNAL(disconnected()), SLOT(disconnected()));
connect(m_socket, SIGNAL(readyRead()), SLOT(readyRead()));
}
@@ -134,6 +135,15 @@ void SocketProcessBackendFactory::readyRead()
\internal
*/
+void SocketProcessBackendFactory::connected()
+{
+ handleConnected();
+}
+
+/*!
+ \internal
+ */
+
void SocketProcessBackendFactory::disconnected()
{
qWarning("Launcher process socket disconnected");
diff --git a/src/core/socketprocessbackendfactory.h b/src/core/socketprocessbackendfactory.h
index e7b3902..b305716 100644
--- a/src/core/socketprocessbackendfactory.h
+++ b/src/core/socketprocessbackendfactory.h
@@ -67,6 +67,7 @@ protected:
private slots:
void readyRead();
+ void connected();
void disconnected();
private:
diff --git a/tests/auto/processmanager/testPipeLauncher/testPipeLauncher.cpp b/tests/auto/processmanager/testPipeLauncher/testPipeLauncher.cpp
index 44ebccc..7dc3051 100644
--- a/tests/auto/processmanager/testPipeLauncher/testPipeLauncher.cpp
+++ b/tests/auto/processmanager/testPipeLauncher/testPipeLauncher.cpp
@@ -38,15 +38,66 @@
****************************************************************************/
#include <QCoreApplication>
+#include <QDebug>
#include "pipelauncher.h"
#include "standardprocessbackendfactory.h"
+#include "prelaunchprocessbackendfactory.h"
+#include "processinfo.h"
QT_USE_NAMESPACE_PROCESSMANAGER
+QString progname;
+
+void usage()
+{
+ qWarning("Usage: %s [ARGS] [OPTS]\n"
+ "\n"
+ "Accepts JSON-formatted commands over STDIN and sends results over STDOUT.\n"
+ "\n"
+ "Valid arguments:\n"
+ " -prelaunch PROGRAM Create prelaunch launcher instead of standard\n"
+ , qPrintable(progname));
+ exit(1);
+}
+
+
int main(int argc, char **argv)
{
QCoreApplication app(argc, argv);
+ QStringList args = QCoreApplication::arguments();
+ progname = args.takeFirst();
+ QString prelaunch_program;
+
+ while (args.size()) {
+ QString arg = args.at(0);
+ if (!arg.startsWith('-'))
+ break;
+ args.removeFirst();
+ if (arg == QStringLiteral("-help"))
+ usage();
+ else if (arg == QStringLiteral("-prelaunch")) {
+ if (!args.size())
+ usage();
+ prelaunch_program = args.takeFirst();
+ }
+ else {
+ qWarning("Unexpected argument '%s'", qPrintable(arg));
+ usage();
+ }
+ }
+
+ if (args.size())
+ usage();
+
PipeLauncher launcher;
- launcher.addFactory(new StandardProcessBackendFactory);
+ if (!prelaunch_program.isEmpty()) {
+ ProcessInfo info;
+ info.setValue("program", prelaunch_program);
+ PrelaunchProcessBackendFactory *factory = new PrelaunchProcessBackendFactory;
+ factory->setProcessInfo(info);
+ launcher.addFactory(factory);
+ }
+ else
+ launcher.addFactory(new StandardProcessBackendFactory);
return app.exec();
}
diff --git a/tests/auto/processmanager/testSocketLauncher/testSocketLauncher.cpp b/tests/auto/processmanager/testSocketLauncher/testSocketLauncher.cpp
index df59fe6..0ae37ef 100644
--- a/tests/auto/processmanager/testSocketLauncher/testSocketLauncher.cpp
+++ b/tests/auto/processmanager/testSocketLauncher/testSocketLauncher.cpp
@@ -47,6 +47,8 @@
#include "socketlauncher.h"
#include "standardprocessbackendfactory.h"
+#include "prelaunchprocessbackendfactory.h"
+#include "processinfo.h"
QT_USE_NAMESPACE_PROCESSMANAGER
@@ -59,6 +61,7 @@ static void usage()
"The socketname is the name of the Unix local socket to listen on\n"
"\n"
"Valid arguments:\n"
+ " -prelaunch PROGRAM Create prelaunch launcher instead of standard\n"
" -validate-inbound PATH Directory where inbound schema are stored\n"
" -validate-outbound PATH Directory where outbound schema are stored\n"
" -warn Warn on invalid messages\n"
@@ -107,14 +110,21 @@ int main(int argc, char **argv)
QCoreApplication app(argc, argv);
QStringList args = QCoreApplication::arguments();
progname = args.takeFirst();
+ QString prelaunch_program;
+
while (args.size()) {
QString arg = args.at(0);
if (!arg.startsWith('-'))
break;
args.removeFirst();
- if (arg == QLatin1String("-help"))
+ if (arg == QStringLiteral("-help"))
usage();
- else if (arg == QLatin1String("-validate-inbound")) {
+ else if (arg == QStringLiteral("-prelaunch")) {
+ if (!args.size())
+ usage();
+ prelaunch_program = args.takeFirst();
+ }
+ else if (arg == QStringLiteral("-validate-inbound")) {
if (!args.size())
usage();
indir = args.takeFirst();
@@ -124,7 +134,7 @@ int main(int argc, char **argv)
exit(1);
}
}
- else if (arg == QLatin1String("-validate-outbound")) {
+ else if (arg == QStringLiteral("-validate-outbound")) {
if (!args.size())
usage();
outdir = args.takeFirst();
@@ -134,9 +144,9 @@ int main(int argc, char **argv)
exit(1);
}
}
- else if (arg == QLatin1String("-warn"))
+ else if (arg == QStringLiteral("-warn"))
flags |= QtAddOn::JsonStream::JsonServer::WarnIfInvalid;
- else if (arg == QLatin1String("-drop"))
+ else if (arg == QStringLiteral("-drop"))
flags |= QtAddOn::JsonStream::JsonServer::DropIfInvalid;
else {
qWarning("Unexpected argument '%s'", qPrintable(arg));
@@ -148,7 +158,16 @@ int main(int argc, char **argv)
usage();
SocketLauncher launcher;
- launcher.addFactory(new StandardProcessBackendFactory);
+ if (!prelaunch_program.isEmpty()) {
+ ProcessInfo info;
+ info.setValue("program", prelaunch_program);
+ PrelaunchProcessBackendFactory *factory = new PrelaunchProcessBackendFactory;
+ factory->setProcessInfo(info);
+ launcher.addFactory(factory);
+ }
+ else
+ launcher.addFactory(new StandardProcessBackendFactory);
+
if (!indir.isEmpty())
loadSchemasFromDirectory(launcher.server()->inboundValidator(), indir);
if (!outdir.isEmpty())
diff --git a/tests/auto/processmanager/tst_processmanager.cpp b/tests/auto/processmanager/tst_processmanager.cpp
index 01686d4..9df9ac7 100644
--- a/tests/auto/processmanager/tst_processmanager.cpp
+++ b/tests/auto/processmanager/tst_processmanager.cpp
@@ -63,6 +63,8 @@ Q_DECLARE_METATYPE(QProcess::ExitStatus);
Q_DECLARE_METATYPE(QProcess::ProcessState);
Q_DECLARE_METATYPE(QProcess::ProcessError);
+const int kProcessCount = 10;
+
/******************************************************************************/
const char *exitStatusToString[] = {
@@ -165,6 +167,8 @@ static void waitForTimeout(int timeout=5000)
static void waitForInternalProcess(ProcessBackendManager *manager, int num=1, int timeout=5000)
{
+ QObject::connect(manager, SIGNAL(internalProcessesChanged()),
+ &QTestEventLoop::instance(), SLOT(exitLoop()));
QTime stopWatch;
stopWatch.start();
forever {
@@ -174,6 +178,8 @@ static void waitForInternalProcess(ProcessBackendManager *manager, int num=1, in
break;
QTestEventLoop::instance().enterLoop(1);
}
+ QObject::disconnect(manager, SIGNAL(internalProcessesChanged()),
+ &QTestEventLoop::instance(), SLOT(exitLoop()));
}
static void waitForInternalProcess(ProcessManager *manager, int num=1, int timeout=5000)
@@ -422,8 +428,6 @@ static void startAndStopClient(ProcessBackendManager *manager, ProcessInfo info,
cleanupProcess(process);
}
-const int kProcessCount = 20;
-
static void startAndStopMultiple(ProcessBackendManager *manager, ProcessInfo info, CommandFunc func)
{
ProcessBackend *plist[kProcessCount];
@@ -486,16 +490,6 @@ static void startAndKillClient(ProcessBackendManager *manager, ProcessInfo info,
cleanupProcess(process);
}
-/*
-static void startAndKillTough(ProcessBackendManager *manager, ProcessInfo info, CommandFunc func)
-{
- Q_UNUSED(func);
- QStringList args = info.arguments();
- args << "-noterm";
- info.setArguments(args);
- startAndKillClient(manager, info, func);
-}
-*/
static void startAndCrashClient(ProcessBackendManager *manager, ProcessInfo info, CommandFunc func)
{
@@ -789,9 +783,8 @@ static void pipeLauncherTest( clientFunc func, infoFunc infoFixup=0 )
static void socketLauncherTest( clientFunc func, QStringList args=QStringList(), infoFunc infoFixup=0 )
{
QProcess *remote = new QProcess;
- QString socketName = QLatin1String("/tmp/socketlauncher");
+ QString socketName = QStringLiteral("/tmp/socketlauncher");
remote->setProcessChannelMode(QProcess::ForwardedChannels);
- qDebug() << "USING ARGS" << args;
remote->start("testSocketLauncher/testSocketLauncher", args << socketName);
QVERIFY(remote->waitForStarted());
waitForSocket(socketName);
@@ -851,12 +844,11 @@ static void preforkLauncherTest( clientFunc func, infoFunc infoFixup=0 )
{
#if defined(Q_OS_LINUX)
QProcess *remote = new QProcess;
- QString socketName = QLatin1String("/tmp/preforklauncher");
+ QString socketName = QStringLiteral("/tmp/preforklauncher");
remote->setProcessChannelMode(QProcess::ForwardedChannels);
QStringList args;
args << "--" << "testPreforkLauncher/testPreforkLauncher" << socketName
<< "--" << "testForkLauncher/testForkLauncher";
- qDebug() << "Trying to run: testPrefork/testPrefork" << args;
remote->start("testPrefork/testPrefork", args);
QVERIFY(remote->waitForStarted());
waitForSocket(socketName);
@@ -988,6 +980,14 @@ private slots:
void prelaunchChildAbort();
void prelaunchWaitIdleTest();
+ void prelaunchForPipeLauncherIdle();
+ void prelaunchForPipeLauncherMemory();
+ void prelaunchForPipeLauncherMultiple();
+
+ void prelaunchForSocketLauncherIdle();
+ void prelaunchForSocketLauncherMemory();
+ void prelaunchForSocketLauncherMultiple();
+
void frontend();
void frontendWaitIdleTest();
void subclassFrontend();
@@ -1007,6 +1007,7 @@ void tst_ProcessManager::prelaunchChildAbort()
{
ProcessBackendManager *manager = new ProcessBackendManager;
TimeoutIdleDelegate *delegate = new TimeoutIdleDelegate;
+ delegate->setIdleInterval(250);
manager->setIdleDelegate(delegate);
ProcessInfo info;
@@ -1032,6 +1033,7 @@ void tst_ProcessManager::prelaunchWaitIdleTest()
{
ProcessBackendManager *manager = new ProcessBackendManager;
TimeoutIdleDelegate *delegate = new TimeoutIdleDelegate;
+ delegate->setIdleInterval(250);
delegate->setEnabled(false);
manager->setIdleDelegate(delegate);
@@ -1050,13 +1052,317 @@ void tst_ProcessManager::prelaunchWaitIdleTest()
delegate->setEnabled(true);
waitForInternalProcess(manager, 1, delegate->idleInterval() + 2000);
QCOMPARE(manager->internalProcesses().count(), 1);
+
+ // Kill the prelaunched process and verify that it is restarted
Q_PID pid = manager->internalProcesses().at(0);
+ ::kill(pid, SIGKILL);
+ waitForInternalProcess(manager, 0);
+ waitForInternalProcess(manager, 1, delegate->idleInterval() + 2000);
+
+ delete manager;
+}
+
+/*
+ The pipe launcher holds a prelaunch backend factory
+ We control the prelaunch process by turning on and off the IdleDelegate
+ */
+
+void tst_ProcessManager::prelaunchForPipeLauncherIdle()
+{
+ ProcessBackendManager *manager = new ProcessBackendManager;
+ TimeoutIdleDelegate *delegate = new TimeoutIdleDelegate;
+ delegate->setIdleInterval(250);
+ delegate->setEnabled(false);
+ manager->setIdleDelegate(delegate);
+
+ ProcessInfo info;
+ info.setValue("program", "testPipeLauncher/testPipeLauncher");
+ info.setValue("arguments",
+ QStringList() << QStringLiteral("-prelaunch")
+ << QStringLiteral("testPrelaunch/testPrelaunch"));
+ PipeProcessBackendFactory *factory = new PipeProcessBackendFactory;
+ factory->setProcessInfo(info);
+ manager->addFactory(factory);
+
+ // Wait for the factory have launched a pipe
+ waitForInternalProcess(manager);
+ QCOMPARE(manager->internalProcesses().count(), 1);
+
+ // Verify that the prelaunch has not started, even after the interval
+ waitForTimeout(delegate->idleInterval() + 2000);
+ QCOMPARE(manager->internalProcesses().count(), 1);
+
+ // Now the prelaunch process should launch
+ delegate->setEnabled(true);
+ waitForInternalProcess(manager, 2, delegate->idleInterval() + 2000);
+ QCOMPARE(manager->internalProcesses().count(), 2);
// Kill the prelaunched process and verify that it is restarted
+ Q_PID pid = manager->internalProcesses().at(1);
+ ::kill(pid, SIGKILL);
+ waitForInternalProcess(manager, 1);
+ waitForInternalProcess(manager, 2, delegate->idleInterval() + 2000);
+
+ // Kill the prelaunched process and keep it dead - otherwise we'll leave a hanging child
+ delegate->setEnabled(false);
+ pid = manager->internalProcesses().at(1);
::kill(pid, SIGKILL);
+ waitForInternalProcess(manager, 1);
+ waitForTimeout(delegate->idleInterval() + 2000);
+ QCOMPARE(manager->internalProcesses().count(), 1);
+
+ delete manager;
+}
+
+/*
+ The pipe launcher holds a prelaunch backend factory
+ We control the prelaunch process by turning on and off MemoryRestricted
+ */
+
+void tst_ProcessManager::prelaunchForPipeLauncherMemory()
+{
+ ProcessBackendManager *manager = new ProcessBackendManager;
+ TimeoutIdleDelegate *delegate = new TimeoutIdleDelegate;
+ delegate->setIdleInterval(250);
+ manager->setIdleDelegate(delegate);
+ manager->setMemoryRestricted(true);
+
+ ProcessInfo info;
+ info.setValue("program", "testPipeLauncher/testPipeLauncher");
+ info.setValue("arguments",
+ QStringList() << QStringLiteral("-prelaunch")
+ << QStringLiteral("testPrelaunch/testPrelaunch"));
+ PipeProcessBackendFactory *factory = new PipeProcessBackendFactory;
+ factory->setProcessInfo(info);
+ manager->addFactory(factory);
+
+ // Wait for the factory have launched a pipe
+ waitForInternalProcess(manager);
+ QCOMPARE(manager->internalProcesses().count(), 1);
+
+ // Verify that the prelaunch has not started, even after the interval
+ waitForTimeout(delegate->idleInterval() + 2000);
+ QCOMPARE(manager->internalProcesses().count(), 1);
+
+ // Now the prelaunch process should launch
+ qDebug() << "Turning off memory restrictions";
+ manager->setMemoryRestricted(false);
+ waitForInternalProcess(manager, 2, delegate->idleInterval() + 2000);
+ QCOMPARE(manager->internalProcesses().count(), 2);
+
+ // Kill the prelaunched process and verify that it is restarted
+ qDebug() << "Directly kill the prelaunched process";
+ Q_PID pid = manager->internalProcesses().at(1);
+ ::kill(pid, SIGKILL);
+ waitForInternalProcess(manager, 1);
+ waitForInternalProcess(manager, 2, delegate->idleInterval() + 2000);
+
+ // Kill the prelaunched process and keep it dead - otherwise we'll leave a hanging child
+ qDebug() << "Turn on memory restrictions";
+ manager->setMemoryRestricted(true);
+ waitForInternalProcess(manager, 1);
+ waitForTimeout(delegate->idleInterval() + 2000);
+ QCOMPARE(manager->internalProcesses().count(), 1);
+
+ delete manager;
+}
+
+/*
+ Each pipe launcher holds a prelaunch backend factory
+ */
+
+void tst_ProcessManager::prelaunchForPipeLauncherMultiple()
+{
+ ProcessBackendManager *manager = new ProcessBackendManager;
+ TimeoutIdleDelegate *delegate = new TimeoutIdleDelegate;
+ delegate->setIdleInterval(250);
+ manager->setMemoryRestricted(true);
+ manager->setIdleDelegate(delegate);
+
+ ProcessInfo info;
+ info.setValue("program", "testPipeLauncher/testPipeLauncher");
+ info.setValue("arguments",
+ QStringList() << QStringLiteral("-prelaunch")
+ << QStringLiteral("testPrelaunch/testPrelaunch"));
+
+ for (int i = 0 ; i < kProcessCount ; i++ ) {
+ PipeProcessBackendFactory *factory = new PipeProcessBackendFactory;
+ factory->setProcessInfo(info);
+ manager->addFactory(factory);
+ }
+
+ // Wait for the factory have launched a pipe
+ qDebug() << "Waiting for" << kProcessCount << "pipe launchers";
+ waitForInternalProcess(manager, kProcessCount);
+
+ // Verify that the prelaunch has not started, even after the interval
+ qDebug() << "Ensure that nothing prelaunches" << manager->internalProcesses().count() << "of" << kProcessCount;
+ waitForTimeout(delegate->idleInterval() + 2000);
+ QCOMPARE(manager->internalProcesses().count(), kProcessCount);
+
+ // Now the prelaunch process should launch
+ qDebug() << "Turn on the prelaunches" << manager->internalProcesses();
+ manager->setMemoryRestricted(false);
+ waitForInternalProcess(manager, 2*kProcessCount, delegate->idleInterval() * kProcessCount + 2000);
+
+ // Shut off the prelaunches
+ qDebug() << "Shut off the prelaunches" << manager->internalProcesses();
+ manager->setMemoryRestricted(true);
+ waitForInternalProcess(manager, kProcessCount, delegate->idleInterval() + 2000);
+
+ delete manager;
+}
+
+
+/*
+ The socket launcher holds a prelaunch backend factory
+ We control the prelaunch process by turning on and off the IdleDelegate
+ */
+
+void tst_ProcessManager::prelaunchForSocketLauncherIdle()
+{
+ QProcess *remote = new QProcess;
+ QString socketName = QStringLiteral("/tmp/socketlauncher");
+ remote->setProcessChannelMode(QProcess::ForwardedChannels);
+ remote->start("testSocketLauncher/testSocketLauncher",
+ QStringList() << QStringLiteral("-prelaunch") << QStringLiteral("testPrelaunch/testPrelaunch")
+ << socketName);
+ QVERIFY(remote->waitForStarted());
+ waitForSocket(socketName);
+
+ ProcessBackendManager *manager = new ProcessBackendManager;
+ TimeoutIdleDelegate *delegate = new TimeoutIdleDelegate;
+ delegate->setIdleInterval(250);
+ delegate->setEnabled(false);
+ manager->setIdleDelegate(delegate);
+
+ SocketProcessBackendFactory *factory = new SocketProcessBackendFactory;
+ factory->setSocketName(socketName);
+ manager->addFactory(factory);
+
+ // Verify that the prelaunch has not started, even after the interval
+ waitForTimeout(delegate->idleInterval() + 2000);
+ QCOMPARE(manager->internalProcesses().count(), 0);
+
+ // Now the prelaunch process should launch
+ delegate->setEnabled(true);
+ waitForInternalProcess(manager, 1, delegate->idleInterval() + 2000);
+ QCOMPARE(manager->internalProcesses().count(), 1);
+
+ // Kill the prelaunched process and verify that it is restarted
+ Q_PID pid = manager->internalProcesses().at(0);
+ ::kill(pid, SIGKILL);
+ waitForInternalProcess(manager, 0);
+ waitForInternalProcess(manager, 1, delegate->idleInterval() + 2000);
+
+ // Kill the prelaunched process and keep it dead - otherwise we'll leave a hanging child
+ delegate->setEnabled(false);
+ pid = manager->internalProcesses().at(0);
+ ::kill(pid, SIGKILL);
+ waitForInternalProcess(manager, 0);
+ waitForTimeout(delegate->idleInterval() + 2000);
+ QCOMPARE(manager->internalProcesses().count(), 0);
+
+ delete manager;
+ delete remote;
+}
+
+/*
+ The socket launcher holds a prelaunch backend factory
+ We control the prelaunch process by turning on and off MemoryRestricted
+ */
+
+void tst_ProcessManager::prelaunchForSocketLauncherMemory()
+{
+ QProcess *remote = new QProcess;
+ QString socketName = QStringLiteral("/tmp/socketlauncher");
+ remote->setProcessChannelMode(QProcess::ForwardedChannels);
+ remote->start("testSocketLauncher/testSocketLauncher",
+ QStringList() << QStringLiteral("-prelaunch") << QStringLiteral("testPrelaunch/testPrelaunch")
+ << socketName);
+ QVERIFY(remote->waitForStarted());
+ waitForSocket(socketName);
+
+ ProcessBackendManager *manager = new ProcessBackendManager;
+ TimeoutIdleDelegate *delegate = new TimeoutIdleDelegate;
+ delegate->setIdleInterval(250);
+ manager->setIdleDelegate(delegate);
+ manager->setMemoryRestricted(true);
+
+ SocketProcessBackendFactory *factory = new SocketProcessBackendFactory;
+ factory->setSocketName(socketName);
+ manager->addFactory(factory);
+
+ // Verify that the prelaunch has not started, even after the interval
+ waitForTimeout(delegate->idleInterval() + 2000);
+ QCOMPARE(manager->internalProcesses().count(), 0);
+
+ // Now the prelaunch process should launch
+ manager->setMemoryRestricted(false);
+ waitForInternalProcess(manager, 1, delegate->idleInterval() + 2000);
+ QCOMPARE(manager->internalProcesses().count(), 1);
+
+ // Kill the prelaunched process and verify that it is restarted
+ Q_PID pid = manager->internalProcesses().at(0);
+ qDebug() << "Killing the prelaunched process" << pid;
+ ::kill(pid, SIGKILL);
+ qDebug() << "Verify that the process count goes to zero";
waitForInternalProcess(manager, 0);
+ qDebug() << "Now wait for the process count to bounce back up";
waitForInternalProcess(manager, 1, delegate->idleInterval() + 2000);
+
+ // Kill the prelaunched process and keep it dead - otherwise we'll leave a hanging child
+ qDebug() << "Set memory to restricted and wait for the process to die";
+ manager->setMemoryRestricted(true);
+ waitForInternalProcess(manager, 0);
+ waitForTimeout(delegate->idleInterval() + 2000);
+ QCOMPARE(manager->internalProcesses().count(), 0);
+
+ delete manager;
+ delete remote;
+}
+
+/*
+ Each socket launcher holds a prelaunch backend factory
+ */
+
+void tst_ProcessManager::prelaunchForSocketLauncherMultiple()
+{
+ QProcess *remote = new QProcess;
+ QString socketName = QStringLiteral("/tmp/socketlauncher");
+ remote->setProcessChannelMode(QProcess::ForwardedChannels);
+ remote->start("testSocketLauncher/testSocketLauncher",
+ QStringList() << QStringLiteral("-prelaunch") << QStringLiteral("testPrelaunch/testPrelaunch")
+ << socketName);
+ QVERIFY(remote->waitForStarted());
+ waitForSocket(socketName);
+
+ ProcessBackendManager *manager = new ProcessBackendManager;
+ TimeoutIdleDelegate *delegate = new TimeoutIdleDelegate;
+ delegate->setIdleInterval(250);
+ manager->setIdleDelegate(delegate);
+ manager->setMemoryRestricted(true);
+
+ for (int i = 0 ; i < kProcessCount ; i++ ) {
+ SocketProcessBackendFactory *factory = new SocketProcessBackendFactory;
+ factory->setSocketName(socketName);
+ manager->addFactory(factory);
+ }
+
+ // Verify that the prelaunch has not started, even after the interval
+ waitForTimeout(delegate->idleInterval() + 2000);
+ QCOMPARE(manager->internalProcesses().count(), 0);
+
+ // Now the prelaunch process should launch
+ manager->setMemoryRestricted(false);
+ waitForInternalProcess(manager, kProcessCount, delegate->idleInterval() * kProcessCount + 2000);
+
+ // Shut off the prelaunches
+ manager->setMemoryRestricted(true);
+ waitForInternalProcess(manager, 0, delegate->idleInterval() + 2000);
+
delete manager;
+ delete remote;
}
void tst_ProcessManager::frontend()