AssignmentClientMonitor doesn't keep a list of its children. Instead it knows about active children due to their entries in the NodeList. Every few seconds, if 2 or more children are idle, the Monitor will ask one to exit

This commit is contained in:
Seth Alves 2015-02-20 13:26:59 -08:00
parent fd357c915c
commit feb0e7ac31
7 changed files with 99 additions and 118 deletions

View file

@ -18,7 +18,7 @@
#include <AccountManager.h> #include <AccountManager.h>
#include <AddressManager.h> #include <AddressManager.h>
#include <Assignment.h> #include <Assignment.h>
#include <AvatarHashMap.h> // #include <AvatarHashMap.h>
#include <HifiConfigVariantMap.h> #include <HifiConfigVariantMap.h>
#include <LogHandler.h> #include <LogHandler.h>
#include <LogUtils.h> #include <LogUtils.h>
@ -41,7 +41,7 @@ SharedAssignmentPointer AssignmentClient::_currentAssignment;
int hifiSockAddrMeta = qRegisterMetaType<HifiSockAddr>("HifiSockAddr"); int hifiSockAddrMeta = qRegisterMetaType<HifiSockAddr>("HifiSockAddr");
AssignmentClient::AssignmentClient(int &argc, char **argv, QUuid nodeUUID) : AssignmentClient::AssignmentClient(int &argc, char **argv) :
QCoreApplication(argc, argv), QCoreApplication(argc, argv),
_assignmentServerHostname(DEFAULT_ASSIGNMENT_SERVER_HOSTNAME), _assignmentServerHostname(DEFAULT_ASSIGNMENT_SERVER_HOSTNAME),
_localASPortSharedMem(NULL), _localASPortSharedMem(NULL),
@ -58,8 +58,11 @@ AssignmentClient::AssignmentClient(int &argc, char **argv, QUuid nodeUUID) :
DependencyManager::registerInheritance<LimitedNodeList, NodeList>(); DependencyManager::registerInheritance<LimitedNodeList, NodeList>();
auto addressManager = DependencyManager::set<AddressManager>(); auto addressManager = DependencyManager::set<AddressManager>();
auto nodeList = DependencyManager::set<NodeList>(NodeType::Unassigned); auto nodeList = DependencyManager::set<NodeList>(NodeType::Unassigned);
auto avatarHashMap = DependencyManager::set<AvatarHashMap>(); // auto avatarHashMap = DependencyManager::set<AvatarHashMap>();
// make up a uuid for this child so the parent can tell us apart. This id will be changed
// when the domain server hands over an assignment.
QUuid nodeUUID = QUuid::createUuid();
nodeList->setSessionUUID(nodeUUID); nodeList->setSessionUUID(nodeUUID);
// setup a shutdown event listener to handle SIGTERM or WM_CLOSE for us // setup a shutdown event listener to handle SIGTERM or WM_CLOSE for us
@ -139,11 +142,37 @@ AssignmentClient::AssignmentClient(int &argc, char **argv, QUuid nodeUUID) :
// Create Singleton objects on main thread // Create Singleton objects on main thread
NetworkAccessManager::getInstance(); NetworkAccessManager::getInstance();
// DependencyManager::get<NetworkAccessManager>();
setUpStatsToMonitor(); setUpStatsToMonitor();
} }
void AssignmentClient::stopAssignmentClient() {
// QList<QThread*> threads = QObject::findChildren <QThread*> ();
// foreach(QThread *thread, threads) { // or FileUploader* fileuploader, fileUploaders_ ?
// qDebug() << "thread " << thread->currentThreadId();
// }
qDebug() << "Exiting.";
_requestTimer.stop();
_statsTimerACM.stop();
// DependencyManager::get<NodeList>()->disconnect();
// DependencyManager::get<NodeList>()->disconnectNotify();
// DependencyManager::get<AddressManager>()->disconnect();
// DependencyManager::get<AddressManager>()->disconnectNotify();
// DependencyManager::destroy<NodeList>();
// DependencyManager::destroy<AddressManager>();
quit();
}
void AssignmentClient::setUpStatsToMonitor() { void AssignmentClient::setUpStatsToMonitor() {
// Figure out the address to send out stats to // Figure out the address to send out stats to
quint16 localMonitorServerPort = DEFAULT_ASSIGNMENT_CLIENT_MONITOR_PORT; quint16 localMonitorServerPort = DEFAULT_ASSIGNMENT_CLIENT_MONITOR_PORT;
@ -249,15 +278,12 @@ void AssignmentClient::readPendingDatagrams() {
qDebug() << "Received an assignment that could not be unpacked. Re-requesting."; qDebug() << "Received an assignment that could not be unpacked. Re-requesting.";
} }
} else if (packetTypeForPacket(receivedPacket) == PacketTypeStopNode) { } else if (packetTypeForPacket(receivedPacket) == PacketTypeStopNode) {
qDebug() << "Network told me to exit"; qDebug() << "Network told me to exit.";
quit(); emit stopAssignmentClient();
} else { } else {
qDebug() << "punt";
// have the NodeList attempt to handle it // have the NodeList attempt to handle it
nodeList->processNodeData(senderSockAddr, receivedPacket); nodeList->processNodeData(senderSockAddr, receivedPacket);
} }
} else {
qDebug() << "packetVersionAndHashMatch said no";
} }
} }
} }

View file

@ -21,7 +21,7 @@ class QSharedMemory;
class AssignmentClient : public QCoreApplication { class AssignmentClient : public QCoreApplication {
Q_OBJECT Q_OBJECT
public: public:
AssignmentClient(int &argc, char **argv, QUuid nodeUUID); AssignmentClient(int &argc, char **argv);
static const SharedAssignmentPointer& getCurrentAssignment() { return _currentAssignment; } static const SharedAssignmentPointer& getCurrentAssignment() { return _currentAssignment; }
private slots: private slots:
@ -30,6 +30,7 @@ private slots:
void assignmentCompleted(); void assignmentCompleted();
void handleAuthenticationRequest(); void handleAuthenticationRequest();
void sendStatsPacketToACM(); void sendStatsPacketToACM();
void stopAssignmentClient();
private: private:
void setUpStatsToMonitor(); void setUpStatsToMonitor();

View file

@ -21,7 +21,7 @@
AssignmentClientApp::AssignmentClientApp(int argc, char* argv[]) : AssignmentClientApp::AssignmentClientApp(int argc, char* argv[]) :
QApplication(argc, argv) QCoreApplication(argc, argv)
{ {
# ifndef WIN32 # ifndef WIN32
setvbuf(stdout, NULL, _IOLBF, 0); setvbuf(stdout, NULL, _IOLBF, 0);
@ -40,9 +40,6 @@ AssignmentClientApp::AssignmentClientApp(int argc, char* argv[]) :
const QCommandLineOption numChildsOption("n", "number of children to fork", "child-count"); const QCommandLineOption numChildsOption("n", "number of children to fork", "child-count");
parser.addOption(numChildsOption); parser.addOption(numChildsOption);
const QCommandLineOption idOption("i", "assignment client id", "uuid");
parser.addOption(idOption);
if (!parser.parse(QCoreApplication::arguments())) { if (!parser.parse(QCoreApplication::arguments())) {
qCritical() << parser.errorText() << endl; qCritical() << parser.errorText() << endl;
parser.showHelp(); parser.showHelp();
@ -54,27 +51,16 @@ AssignmentClientApp::AssignmentClientApp(int argc, char* argv[]) :
Q_UNREACHABLE(); Q_UNREACHABLE();
} }
if (parser.isSet(numChildsOption) && parser.isSet(idOption)) {
qCritical() << "using both -i and -n doesn't make sense.";
parser.showHelp();
Q_UNREACHABLE();
}
unsigned int numForks = 0; unsigned int numForks = 0;
if (parser.isSet(numChildsOption)) { if (parser.isSet(numChildsOption)) {
numForks = parser.value(numChildsOption).toInt(); numForks = parser.value(numChildsOption).toInt();
} }
QUuid nodeUUID = QUuid::createUuid();
if (parser.isSet(idOption)) {
nodeUUID = QUuid(parser.value(idOption));
}
if (numForks) { if (numForks) {
AssignmentClientMonitor monitor(argc, argv, numForks); AssignmentClientMonitor monitor(argc, argv, numForks);
monitor.exec(); monitor.exec();
} else { } else {
AssignmentClient client(argc, argv, nodeUUID); AssignmentClient client(argc, argv);
client.exec(); client.exec();
} }
} }

View file

@ -11,7 +11,7 @@
#include <QApplication> #include <QApplication>
class AssignmentClientApp : public QApplication { class AssignmentClientApp : public QCoreApplication {
Q_OBJECT Q_OBJECT
public: public:
AssignmentClientApp(int argc, char* argv[]); AssignmentClientApp(int argc, char* argv[]);

View file

@ -20,6 +20,7 @@
#include "AssignmentClientMonitor.h" #include "AssignmentClientMonitor.h"
#include "PacketHeaders.h" #include "PacketHeaders.h"
#include "SharedUtil.h"
const char* NUM_FORKS_PARAMETER = "-n"; const char* NUM_FORKS_PARAMETER = "-n";
@ -70,83 +71,47 @@ AssignmentClientMonitor::AssignmentClientMonitor(int &argc, char **argv, const u
AssignmentClientMonitor::~AssignmentClientMonitor() { AssignmentClientMonitor::~AssignmentClientMonitor() {
stopChildProcesses(); stopChildProcesses();
foreach (AssignmentClientChildData* childStatus, _childStatus) {
delete childStatus;
} }
}
void AssignmentClientMonitor::stopChildProcesses() { void AssignmentClientMonitor::stopChildProcesses() {
auto nodeList = DependencyManager::get<NodeList>();
QList<QPointer<QProcess> >::Iterator it = _childProcesses.begin(); nodeList->eachNode([&](const SharedNodePointer& node){
while (it != _childProcesses.end()) { qDebug() << "asking child" << node->getUUID() << "to exit.";
if (!it->isNull()) { node->activateLocalSocket();
qDebug() << "Monitor is terminating child process" << it->data(); QByteArray diePacket = byteArrayWithPopulatedHeader(PacketTypeStopNode);
nodeList->writeUnverifiedDatagram(diePacket, *node->getActiveSocket());
// don't re-spawn this child when it goes down });
disconnect(it->data(), 0, this, 0);
it->data()->terminate();
it->data()->waitForFinished();
}
it = _childProcesses.erase(it);
}
} }
void AssignmentClientMonitor::spawnChildClient() { void AssignmentClientMonitor::spawnChildClient() {
QProcess *assignmentClient = new QProcess(this); QProcess *assignmentClient = new QProcess(this);
_childProcesses.append(QPointer<QProcess>(assignmentClient));
QUuid childUUID = QUuid::createUuid();
// create a Node for this child. this is done so we can idenitfy packets from unknown children
DependencyManager::get<LimitedNodeList>()->addOrUpdateNode
(childUUID, NodeType::Unassigned, HifiSockAddr("localhost", 0), HifiSockAddr("localhost", 0), false);
// make sure that the output from the child process appears in our output // make sure that the output from the child process appears in our output
assignmentClient->setProcessChannelMode(QProcess::ForwardedChannels); assignmentClient->setProcessChannelMode(QProcess::ForwardedChannels);
QStringList idArgs = QStringList() << "-i" << childUUID.toString(); assignmentClient->start(applicationFilePath(), _childArguments);
assignmentClient->start(applicationFilePath(), _childArguments + idArgs);
// link the child processes' finished slot to our childProcessFinished slot
connect(assignmentClient, SIGNAL(finished(int, QProcess::ExitStatus)), this,
SLOT(childProcessFinished(int, QProcess::ExitStatus)));
qDebug() << "Spawned a child client with PID" << assignmentClient->pid(); qDebug() << "Spawned a child client with PID" << assignmentClient->pid();
} }
void AssignmentClientMonitor::childProcessFinished(int exitCode, QProcess::ExitStatus exitStatus) {
qDebug("Replacing dead child assignment client with a new one");
// remove the old process from our list of child processes
qDebug() << "need to remove" << QPointer<QProcess>(qobject_cast<QProcess*>(sender()));
_childProcesses.removeOne(QPointer<QProcess>(qobject_cast<QProcess*>(sender())));
spawnChildClient();
}
void AssignmentClientMonitor::checkSpares() { void AssignmentClientMonitor::checkSpares() {
qDebug() << "check spares:"; auto nodeList = DependencyManager::get<NodeList>();
QUuid aSpareId = "";
QString aSpareId = "";
unsigned int spareCount = 0; unsigned int spareCount = 0;
QHash<QString, AssignmentClientChildData*>::const_iterator i = _childStatus.constBegin(); nodeList->removeSilentNodes();
while (i != _childStatus.constEnd()) {
qDebug() << " " << i.key() << i.value()->getChildType(); qDebug() << "check spares:";
if (i.value()->getChildType() == "none") {
nodeList->eachNode([&](const SharedNodePointer& node){
AssignmentClientChildData *childData = static_cast<AssignmentClientChildData*>(node->getLinkedData());
qDebug() << " " << node->getUUID() << childData->getChildType();
if (childData->getChildType() == "none") {
spareCount ++; spareCount ++;
aSpareId = i.key(); aSpareId = node->getUUID();
}
++i;
} }
});
qDebug() << " spare count is" << spareCount; qDebug() << " spare count is" << spareCount;
@ -156,13 +121,16 @@ void AssignmentClientMonitor::checkSpares() {
} }
if (spareCount > 1) { if (spareCount > 1) {
qDebug() << "KILL"; // kill aSpareId
qDebug() << "asking child" << aSpareId << "to exit.";
SharedNodePointer childNode = nodeList->nodeWithUUID(aSpareId);
childNode->activateLocalSocket();
QByteArray diePacket = byteArrayWithPopulatedHeader(PacketTypeStopNode);
nodeList->writeUnverifiedDatagram(diePacket, childNode);
} }
} }
void AssignmentClientMonitor::readPendingDatagrams() { void AssignmentClientMonitor::readPendingDatagrams() {
auto nodeList = DependencyManager::get<NodeList>(); auto nodeList = DependencyManager::get<NodeList>();
@ -176,17 +144,22 @@ void AssignmentClientMonitor::readPendingDatagrams() {
if (nodeList->packetVersionAndHashMatch(receivedPacket)) { if (nodeList->packetVersionAndHashMatch(receivedPacket)) {
if (packetTypeForPacket(receivedPacket) == PacketTypeNodeJsonStats) { if (packetTypeForPacket(receivedPacket) == PacketTypeNodeJsonStats) {
QUuid packetUUID = uuidFromPacketHeader(receivedPacket); QUuid packetUUID = uuidFromPacketHeader(receivedPacket);
// qDebug() << "packetUUID = " << packetUUID;
SharedNodePointer matchingNode = nodeList->sendingNodeForPacket(receivedPacket); SharedNodePointer matchingNode = nodeList->sendingNodeForPacket(receivedPacket);
if (!matchingNode) { if (!matchingNode) {
qDebug() << "got packet from unknown child, id =" << packetUUID.toString(); // XXX only do this if from local machine
if (!packetUUID.isNull()) {
matchingNode = DependencyManager::get<LimitedNodeList>()->addOrUpdateNode
(packetUUID, NodeType::Unassigned, senderSockAddr, senderSockAddr, false);
AssignmentClientChildData *childData = new AssignmentClientChildData("unknown");
matchingNode->setLinkedData(childData);
} else {
// tell unknown assignment-client child to exit. // tell unknown assignment-client child to exit.
qDebug() << "asking unknown child to exit.";
QByteArray diePacket = byteArrayWithPopulatedHeader(PacketTypeStopNode); QByteArray diePacket = byteArrayWithPopulatedHeader(PacketTypeStopNode);
nodeList->writeUnverifiedDatagram(diePacket, senderSockAddr); nodeList->writeUnverifiedDatagram(diePacket, senderSockAddr);
} }
}
if (matchingNode) { if (matchingNode) {
// update our records about how to reach this child // update our records about how to reach this child
@ -195,25 +168,18 @@ void AssignmentClientMonitor::readPendingDatagrams() {
// push past the packet header // push past the packet header
QDataStream packetStream(receivedPacket); QDataStream packetStream(receivedPacket);
packetStream.skipRawData(numBytesForPacketHeader(receivedPacket)); packetStream.skipRawData(numBytesForPacketHeader(receivedPacket));
// decode json
QVariantMap unpackedVariantMap; QVariantMap unpackedVariantMap;
packetStream >> unpackedVariantMap; packetStream >> unpackedVariantMap;
QJsonObject unpackedStatsJSON = QJsonObject::fromVariantMap(unpackedVariantMap); QJsonObject unpackedStatsJSON = QJsonObject::fromVariantMap(unpackedVariantMap);
// qDebug() << "ACM got stats packet, id =" << packetUUID.toString() // get child's assignment type out of the decoded json
// << "type =" << unpackedStatsJSON["assignment_type"];
QString key(QString(packetUUID.toString()));
if (_childStatus.contains(key)) {
delete _childStatus[ key ];
}
QString childType = unpackedStatsJSON["assignment_type"].toString(); QString childType = unpackedStatsJSON["assignment_type"].toString();
auto childStatus = new AssignmentClientChildData(childType); AssignmentClientChildData *childData =
_childStatus[ key ] = childStatus; static_cast<AssignmentClientChildData*>(matchingNode->getLinkedData());
childData->setChildType(childType);
// note when this child talked
matchingNode->setLastHeardMicrostamp(usecTimestampNow());
} }
} else { } else {
// have the NodeList attempt to handle it // have the NodeList attempt to handle it

View file

@ -15,21 +15,25 @@
#include <QtCore/QCoreApplication> #include <QtCore/QCoreApplication>
#include <QtCore/qpointer.h> #include <QtCore/qpointer.h>
#include <QtCore/QProcess> #include <QtCore/QProcess>
#include <QtCore/QDateTime>
#include <Assignment.h> #include <Assignment.h>
extern const char* NUM_FORKS_PARAMETER; extern const char* NUM_FORKS_PARAMETER;
class AssignmentClientChildData { class AssignmentClientChildData : public NodeData {
public: public:
AssignmentClientChildData(QString childType); AssignmentClientChildData(QString childType);
~AssignmentClientChildData(); ~AssignmentClientChildData();
QString getChildType() { return _childType; } QString getChildType() { return _childType; }
void setChildType(QString childType) { _childType = childType; }
// implement parseData to return 0 so we can be a subclass of NodeData
int parseData(const QByteArray& packet) { return 0; }
private: private:
QString _childType; QString _childType;
// ... timestamp
}; };
@ -41,17 +45,15 @@ public:
void stopChildProcesses(); void stopChildProcesses();
private slots: private slots:
void childProcessFinished(int exitCode, QProcess::ExitStatus exitStatus); // void childProcessFinished(int exitCode, QProcess::ExitStatus exitStatus);
void readPendingDatagrams(); void readPendingDatagrams();
void checkSpares(); void checkSpares();
private: private:
void spawnChildClient(); void spawnChildClient();
QList<QPointer<QProcess> > _childProcesses; // QList<QPointer<QProcess> > _childProcesses;
QStringList _childArguments; QStringList _childArguments;
QHash<QString, AssignmentClientChildData*> _childStatus;
QTimer _checkSparesTimer; // every few seconds see if it need fewer or more spare children QTimer _checkSparesTimer; // every few seconds see if it need fewer or more spare children
}; };

View file

@ -76,7 +76,7 @@ const QString AddressManager::currentPath(bool withOrientation) const {
pathString += "/" + orientationString; pathString += "/" + orientationString;
} else { } else {
qDebug() << "Cannot add orientation to path without a getter for position." qDebug() << "Cannot add orientation to path without a getter for position."
<< "Call AdressManager::setOrientationGetter to pass a function that will return a glm::quat"; << "Call AddressManager::setOrientationGetter to pass a function that will return a glm::quat";
} }
} }
@ -84,7 +84,7 @@ const QString AddressManager::currentPath(bool withOrientation) const {
return pathString; return pathString;
} else { } else {
qDebug() << "Cannot create address path without a getter for position." qDebug() << "Cannot create address path without a getter for position."
<< "Call AdressManager::setPositionGetter to pass a function that will return a const glm::vec3&"; << "Call AddressManager::setPositionGetter to pass a function that will return a const glm::vec3&";
return QString(); return QString();
} }
} }