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 <AddressManager.h>
#include <Assignment.h>
#include <AvatarHashMap.h>
// #include <AvatarHashMap.h>
#include <HifiConfigVariantMap.h>
#include <LogHandler.h>
#include <LogUtils.h>
@ -41,7 +41,7 @@ SharedAssignmentPointer AssignmentClient::_currentAssignment;
int hifiSockAddrMeta = qRegisterMetaType<HifiSockAddr>("HifiSockAddr");
AssignmentClient::AssignmentClient(int &argc, char **argv, QUuid nodeUUID) :
AssignmentClient::AssignmentClient(int &argc, char **argv) :
QCoreApplication(argc, argv),
_assignmentServerHostname(DEFAULT_ASSIGNMENT_SERVER_HOSTNAME),
_localASPortSharedMem(NULL),
@ -58,8 +58,11 @@ AssignmentClient::AssignmentClient(int &argc, char **argv, QUuid nodeUUID) :
DependencyManager::registerInheritance<LimitedNodeList, NodeList>();
auto addressManager = DependencyManager::set<AddressManager>();
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);
// 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
NetworkAccessManager::getInstance();
// DependencyManager::get<NetworkAccessManager>();
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() {
// Figure out the address to send out stats to
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.";
}
} else if (packetTypeForPacket(receivedPacket) == PacketTypeStopNode) {
qDebug() << "Network told me to exit";
quit();
qDebug() << "Network told me to exit.";
emit stopAssignmentClient();
} else {
qDebug() << "punt";
// have the NodeList attempt to handle it
nodeList->processNodeData(senderSockAddr, receivedPacket);
}
} else {
qDebug() << "packetVersionAndHashMatch said no";
}
}
}

View file

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

View file

@ -21,7 +21,7 @@
AssignmentClientApp::AssignmentClientApp(int argc, char* argv[]) :
QApplication(argc, argv)
QCoreApplication(argc, argv)
{
# ifndef WIN32
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");
parser.addOption(numChildsOption);
const QCommandLineOption idOption("i", "assignment client id", "uuid");
parser.addOption(idOption);
if (!parser.parse(QCoreApplication::arguments())) {
qCritical() << parser.errorText() << endl;
parser.showHelp();
@ -54,27 +51,16 @@ AssignmentClientApp::AssignmentClientApp(int argc, char* argv[]) :
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;
if (parser.isSet(numChildsOption)) {
numForks = parser.value(numChildsOption).toInt();
}
QUuid nodeUUID = QUuid::createUuid();
if (parser.isSet(idOption)) {
nodeUUID = QUuid(parser.value(idOption));
}
if (numForks) {
AssignmentClientMonitor monitor(argc, argv, numForks);
monitor.exec();
} else {
AssignmentClient client(argc, argv, nodeUUID);
AssignmentClient client(argc, argv);
client.exec();
}
}

View file

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

View file

@ -20,6 +20,7 @@
#include "AssignmentClientMonitor.h"
#include "PacketHeaders.h"
#include "SharedUtil.h"
const char* NUM_FORKS_PARAMETER = "-n";
@ -70,85 +71,49 @@ AssignmentClientMonitor::AssignmentClientMonitor(int &argc, char **argv, const u
AssignmentClientMonitor::~AssignmentClientMonitor() {
stopChildProcesses();
foreach (AssignmentClientChildData* childStatus, _childStatus) {
delete childStatus;
}
}
void AssignmentClientMonitor::stopChildProcesses() {
QList<QPointer<QProcess> >::Iterator it = _childProcesses.begin();
while (it != _childProcesses.end()) {
if (!it->isNull()) {
qDebug() << "Monitor is terminating child process" << it->data();
// 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);
}
auto nodeList = DependencyManager::get<NodeList>();
nodeList->eachNode([&](const SharedNodePointer& node){
qDebug() << "asking child" << node->getUUID() << "to exit.";
node->activateLocalSocket();
QByteArray diePacket = byteArrayWithPopulatedHeader(PacketTypeStopNode);
nodeList->writeUnverifiedDatagram(diePacket, *node->getActiveSocket());
});
}
void AssignmentClientMonitor::spawnChildClient() {
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
assignmentClient->setProcessChannelMode(QProcess::ForwardedChannels);
QStringList idArgs = QStringList() << "-i" << childUUID.toString();
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)));
assignmentClient->start(applicationFilePath(), _childArguments);
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() {
qDebug() << "check spares:";
QString aSpareId = "";
auto nodeList = DependencyManager::get<NodeList>();
QUuid aSpareId = "";
unsigned int spareCount = 0;
QHash<QString, AssignmentClientChildData*>::const_iterator i = _childStatus.constBegin();
while (i != _childStatus.constEnd()) {
qDebug() << " " << i.key() << i.value()->getChildType();
if (i.value()->getChildType() == "none") {
spareCount ++;
aSpareId = i.key();
}
++i;
}
nodeList->removeSilentNodes();
qDebug() << "spare count is" << spareCount;
qDebug() << "check spares:";
nodeList->eachNode([&](const SharedNodePointer& node){
AssignmentClientChildData *childData = static_cast<AssignmentClientChildData*>(node->getLinkedData());
qDebug() << " " << node->getUUID() << childData->getChildType();
if (childData->getChildType() == "none") {
spareCount ++;
aSpareId = node->getUUID();
}
});
qDebug() << " spare count is" << spareCount;
if (spareCount < 1) {
qDebug() << "FORK";
@ -156,13 +121,16 @@ void AssignmentClientMonitor::checkSpares() {
}
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() {
auto nodeList = DependencyManager::get<NodeList>();
@ -176,16 +144,21 @@ void AssignmentClientMonitor::readPendingDatagrams() {
if (nodeList->packetVersionAndHashMatch(receivedPacket)) {
if (packetTypeForPacket(receivedPacket) == PacketTypeNodeJsonStats) {
QUuid packetUUID = uuidFromPacketHeader(receivedPacket);
// qDebug() << "packetUUID = " << packetUUID;
SharedNodePointer matchingNode = nodeList->sendingNodeForPacket(receivedPacket);
if (!matchingNode) {
qDebug() << "got packet from unknown child, id =" << packetUUID.toString();
// tell unknown assignment-client child to exit.
QByteArray diePacket = byteArrayWithPopulatedHeader(PacketTypeStopNode);
nodeList->writeUnverifiedDatagram(diePacket, senderSockAddr);
// 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.
qDebug() << "asking unknown child to exit.";
QByteArray diePacket = byteArrayWithPopulatedHeader(PacketTypeStopNode);
nodeList->writeUnverifiedDatagram(diePacket, senderSockAddr);
}
}
if (matchingNode) {
@ -195,25 +168,18 @@ void AssignmentClientMonitor::readPendingDatagrams() {
// push past the packet header
QDataStream packetStream(receivedPacket);
packetStream.skipRawData(numBytesForPacketHeader(receivedPacket));
// decode json
QVariantMap unpackedVariantMap;
packetStream >> unpackedVariantMap;
QJsonObject unpackedStatsJSON = QJsonObject::fromVariantMap(unpackedVariantMap);
// qDebug() << "ACM got stats packet, id =" << packetUUID.toString()
// << "type =" << unpackedStatsJSON["assignment_type"];
QString key(QString(packetUUID.toString()));
if (_childStatus.contains(key)) {
delete _childStatus[ key ];
}
// get child's assignment type out of the decoded json
QString childType = unpackedStatsJSON["assignment_type"].toString();
auto childStatus = new AssignmentClientChildData(childType);
_childStatus[ key ] = childStatus;
AssignmentClientChildData *childData =
static_cast<AssignmentClientChildData*>(matchingNode->getLinkedData());
childData->setChildType(childType);
// note when this child talked
matchingNode->setLastHeardMicrostamp(usecTimestampNow());
}
} else {
// have the NodeList attempt to handle it

View file

@ -15,21 +15,25 @@
#include <QtCore/QCoreApplication>
#include <QtCore/qpointer.h>
#include <QtCore/QProcess>
#include <QtCore/QDateTime>
#include <Assignment.h>
extern const char* NUM_FORKS_PARAMETER;
class AssignmentClientChildData {
class AssignmentClientChildData : public NodeData {
public:
AssignmentClientChildData(QString childType);
~AssignmentClientChildData();
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:
QString _childType;
// ... timestamp
};
@ -41,17 +45,15 @@ public:
void stopChildProcesses();
private slots:
void childProcessFinished(int exitCode, QProcess::ExitStatus exitStatus);
// void childProcessFinished(int exitCode, QProcess::ExitStatus exitStatus);
void readPendingDatagrams();
void checkSpares();
private:
void spawnChildClient();
QList<QPointer<QProcess> > _childProcesses;
// QList<QPointer<QProcess> > _childProcesses;
QStringList _childArguments;
QHash<QString, AssignmentClientChildData*> _childStatus;
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;
} else {
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;
} else {
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();
}
}