From bb073bdff3f4a2519ed2ae0c83cfe9549dd47b9b Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Mon, 9 Sep 2013 11:59:58 -0700 Subject: [PATCH 1/3] fork off n children and keep the parent process as a monitor --- assignment-client/src/main.cpp | 126 ++++++++++++++++++--------------- 1 file changed, 68 insertions(+), 58 deletions(-) diff --git a/assignment-client/src/main.cpp b/assignment-client/src/main.cpp index 67d2a1e021..72ae826ef2 100644 --- a/assignment-client/src/main.cpp +++ b/assignment-client/src/main.cpp @@ -32,16 +32,19 @@ int main(int argc, const char* argv[]) { } const char* NUM_FORKS_PARAMETER = "-n"; - const char* numForksIncludingParentString = getCmdOption(argc, argv, NUM_FORKS_PARAMETER); + const char* numForksString = getCmdOption(argc, argv, NUM_FORKS_PARAMETER); - if (numForksIncludingParentString) { - int numForksIncludingParent = atoi(numForksIncludingParentString); - qDebug() << "Starting" << numForksIncludingParent << "assignment clients."; + int processID = 0; + int numForks = 0; + + if (numForksString) { + numForks = atoi(numForksString); + qDebug() << "Starting" << numForks << "assignment clients."; + - int processID = 0; // fire off as many children as we need (this is one less than the parent since the parent will run as well) - for (int i = 0; i < numForksIncludingParent - 1; i++) { + for (int i = 0; i < numForks; i++) { processID = fork(); if (processID == 0) { @@ -51,63 +54,70 @@ int main(int argc, const char* argv[]) { } } - // create a NodeList as an unassigned client - NodeList* nodeList = NodeList::createInstance(NODE_TYPE_UNASSIGNED); - - // set the custom assignment socket if we have it - if (customAssignmentSocket.sin_addr.s_addr != 0) { - nodeList->setAssignmentServerSocket((sockaddr*) &customAssignmentSocket); - } - - // change the timeout on the nodelist socket to be as often as we want to re-request - nodeList->getNodeSocket()->setBlockingReceiveTimeoutInUsecs(ASSIGNMENT_REQUEST_INTERVAL_USECS); - - timeval lastRequest = {}; - - unsigned char packetData[MAX_PACKET_SIZE]; - ssize_t receivedBytes = 0; - - // grab the assignment pool from argv, if it was passed - const char* ASSIGNMENT_POOL_PARAMETER = "-p"; - const char* assignmentPool = getCmdOption(argc, argv, ASSIGNMENT_POOL_PARAMETER); - - // create a request assignment, accept all assignments, pass the desired pool (if it exists) - Assignment requestAssignment(Assignment::Request, Assignment::All, assignmentPool); - - while (true) { - if (usecTimestampNow() - usecTimestamp(&lastRequest) >= ASSIGNMENT_REQUEST_INTERVAL_USECS) { - gettimeofday(&lastRequest, NULL); - // if we're here we have no assignment, so send a request - nodeList->sendAssignment(requestAssignment); + if (processID == 0 || numForks == 0) { + // this is one of the child forks or there is a single assignment client, continue assignment-client execution + + // create a NodeList as an unassigned client + NodeList* nodeList = NodeList::createInstance(NODE_TYPE_UNASSIGNED); + + // set the custom assignment socket if we have it + if (customAssignmentSocket.sin_addr.s_addr != 0) { + nodeList->setAssignmentServerSocket((sockaddr*) &customAssignmentSocket); } - if (nodeList->getNodeSocket()->receive(packetData, &receivedBytes) && - packetData[0] == PACKET_TYPE_DEPLOY_ASSIGNMENT && packetVersionMatch(packetData)) { + // change the timeout on the nodelist socket to be as often as we want to re-request + nodeList->getNodeSocket()->setBlockingReceiveTimeoutInUsecs(ASSIGNMENT_REQUEST_INTERVAL_USECS); + + timeval lastRequest = {}; + + unsigned char packetData[MAX_PACKET_SIZE]; + ssize_t receivedBytes = 0; + + // grab the assignment pool from argv, if it was passed + const char* ASSIGNMENT_POOL_PARAMETER = "-p"; + const char* assignmentPool = getCmdOption(argc, argv, ASSIGNMENT_POOL_PARAMETER); + + // create a request assignment, accept all assignments, pass the desired pool (if it exists) + Assignment requestAssignment(Assignment::Request, Assignment::All, assignmentPool); + + while (true) { + if (usecTimestampNow() - usecTimestamp(&lastRequest) >= ASSIGNMENT_REQUEST_INTERVAL_USECS) { + gettimeofday(&lastRequest, NULL); + // if we're here we have no assignment, so send a request + nodeList->sendAssignment(requestAssignment); + } - // construct the deployed assignment from the packet data - Assignment deployedAssignment(packetData, receivedBytes); - - qDebug() << "Received an assignment - " << deployedAssignment << "\n"; - - // switch our nodelist DOMAIN_IP to the ip receieved in the assignment - if (deployedAssignment.getDomainSocket()->sa_family == AF_INET) { - in_addr domainSocketAddr = ((sockaddr_in*) deployedAssignment.getDomainSocket())->sin_addr; - nodeList->setDomainIP(inet_ntoa(domainSocketAddr)); + if (nodeList->getNodeSocket()->receive(packetData, &receivedBytes) && + packetData[0] == PACKET_TYPE_DEPLOY_ASSIGNMENT && packetVersionMatch(packetData)) { - qDebug() << "Changed domain IP to " << inet_ntoa(domainSocketAddr); + // construct the deployed assignment from the packet data + Assignment deployedAssignment(packetData, receivedBytes); + + qDebug() << "Received an assignment - " << deployedAssignment << "\n"; + + // switch our nodelist DOMAIN_IP to the ip receieved in the assignment + if (deployedAssignment.getDomainSocket()->sa_family == AF_INET) { + in_addr domainSocketAddr = ((sockaddr_in*) deployedAssignment.getDomainSocket())->sin_addr; + nodeList->setDomainIP(inet_ntoa(domainSocketAddr)); + + qDebug() << "Changed domain IP to " << inet_ntoa(domainSocketAddr); + } + + if (deployedAssignment.getType() == Assignment::AudioMixer) { + AudioMixer::run(); + } else { + AvatarMixer::run(); + } + + qDebug() << "Assignment finished or never started - waiting for new assignment"; + + // reset our NodeList by switching back to unassigned and clearing the list + nodeList->setOwnerType(NODE_TYPE_UNASSIGNED); + nodeList->clear(); } - - if (deployedAssignment.getType() == Assignment::AudioMixer) { - AudioMixer::run(); - } else { - AvatarMixer::run(); - } - - qDebug() << "Assignment finished or never started - waiting for new assignment"; - - // reset our NodeList by switching back to unassigned and clearing the list - nodeList->setOwnerType(NODE_TYPE_UNASSIGNED); - nodeList->clear(); } + } else { + // don't bail until all children have finished + wait(NULL); } } \ No newline at end of file From f770dd623bb4d69c9fc7c8dee6b03ed0d39e5969 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Mon, 9 Sep 2013 12:31:53 -0700 Subject: [PATCH 2/3] have parent assignment-client make sure there are always n --- assignment-client/src/main.cpp | 207 ++++++++++++++++++++++----------- 1 file changed, 137 insertions(+), 70 deletions(-) diff --git a/assignment-client/src/main.cpp b/assignment-client/src/main.cpp index 72ae826ef2..50e4f069a5 100644 --- a/assignment-client/src/main.cpp +++ b/assignment-client/src/main.cpp @@ -7,6 +7,8 @@ // #include +#include +#include #include #include @@ -18,106 +20,171 @@ const long long ASSIGNMENT_REQUEST_INTERVAL_USECS = 1 * 1000 * 1000; +pid_t* childForks = NULL; +sockaddr_in customAssignmentSocket = {}; +const char* assignmentPool = NULL; +int numForks = 0; + +void childClient() { + // this is one of the child forks or there is a single assignment client, continue assignment-client execution + + // create a NodeList as an unassigned client + NodeList* nodeList = NodeList::createInstance(NODE_TYPE_UNASSIGNED); + + // set the custom assignment socket if we have it + if (customAssignmentSocket.sin_addr.s_addr != 0) { + nodeList->setAssignmentServerSocket((sockaddr*) &customAssignmentSocket); + } + + // change the timeout on the nodelist socket to be as often as we want to re-request + nodeList->getNodeSocket()->setBlockingReceiveTimeoutInUsecs(ASSIGNMENT_REQUEST_INTERVAL_USECS); + + timeval lastRequest = {}; + + unsigned char packetData[MAX_PACKET_SIZE]; + ssize_t receivedBytes = 0; + + // create a request assignment, accept all assignments, pass the desired pool (if it exists) + Assignment requestAssignment(Assignment::Request, Assignment::All, assignmentPool); + + while (true) { + if (usecTimestampNow() - usecTimestamp(&lastRequest) >= ASSIGNMENT_REQUEST_INTERVAL_USECS) { + gettimeofday(&lastRequest, NULL); + // if we're here we have no assignment, so send a request + nodeList->sendAssignment(requestAssignment); + } + + if (nodeList->getNodeSocket()->receive(packetData, &receivedBytes) && + packetData[0] == PACKET_TYPE_DEPLOY_ASSIGNMENT && packetVersionMatch(packetData)) { + + // construct the deployed assignment from the packet data + Assignment deployedAssignment(packetData, receivedBytes); + + qDebug() << "Received an assignment - " << deployedAssignment << "\n"; + + // switch our nodelist DOMAIN_IP to the ip receieved in the assignment + if (deployedAssignment.getDomainSocket()->sa_family == AF_INET) { + in_addr domainSocketAddr = ((sockaddr_in*) deployedAssignment.getDomainSocket())->sin_addr; + nodeList->setDomainIP(inet_ntoa(domainSocketAddr)); + + qDebug() << "Changed domain IP to " << inet_ntoa(domainSocketAddr); + } + + if (deployedAssignment.getType() == Assignment::AudioMixer) { + AudioMixer::run(); + } else { + AvatarMixer::run(); + } + + qDebug() << "Assignment finished or never started - waiting for new assignment"; + + // reset our NodeList by switching back to unassigned and clearing the list + nodeList->setOwnerType(NODE_TYPE_UNASSIGNED); + nodeList->clear(); + } + } +} + +void sigchldHandler(int sig) { + pid_t processID; + int status; + + while ((processID = waitpid(-1, &status, WNOHANG)) != -1) { + if (processID == 0) { + // there are no more children to process, break out of here + break; + } + + qDebug() << "Handling death of" << processID; + + int newForkProcessID = 0; + + // find the dead process in the array of child forks + for (int i = 0; i < ::numForks; i++) { + if (::childForks[i] == processID) { + qDebug() << "Matched" << ::childForks[i] << "with" << processID; + + newForkProcessID = fork(); + if (newForkProcessID == 0) { + // this is the child, call childClient + childClient(); + + // break out so we don't fork bomb + break; + } else { + // this is the parent, replace the dead process with the new one + ::childForks[i] = newForkProcessID; + break; + } + } + } + } +} + +void parentMonitor() { + + struct sigaction sa; + + memset(&sa, 0, sizeof(sa)); + sa.sa_handler = sigchldHandler; + + sigaction(SIGCHLD, &sa, NULL); + + pid_t childID = 0; + + // don't bail until all children have finished + while ((childID = waitpid(-1, NULL, 0))) { + if (errno == ECHILD) { + break; + } + } + + // delete the array of pid_t holding the forked process IDs + delete[] ::childForks; +} int main(int argc, const char* argv[]) { setvbuf(stdout, NULL, _IOLBF, 0); - sockaddr_in customAssignmentSocket = {}; - // grab the overriden assignment-server hostname from argv, if it exists const char* customAssignmentServer = getCmdOption(argc, argv, "-a"); if (customAssignmentServer) { - customAssignmentSocket = socketForHostnameAndHostOrderPort(customAssignmentServer, ASSIGNMENT_SERVER_PORT); + ::customAssignmentSocket = socketForHostnameAndHostOrderPort(customAssignmentServer, ASSIGNMENT_SERVER_PORT); } const char* NUM_FORKS_PARAMETER = "-n"; const char* numForksString = getCmdOption(argc, argv, NUM_FORKS_PARAMETER); + // grab the assignment pool from argv, if it was passed + const char* ASSIGNMENT_POOL_PARAMETER = "-p"; + ::assignmentPool = getCmdOption(argc, argv, ASSIGNMENT_POOL_PARAMETER); + int processID = 0; - int numForks = 0; if (numForksString) { - numForks = atoi(numForksString); + ::numForks = atoi(numForksString); qDebug() << "Starting" << numForks << "assignment clients."; - + ::childForks = new pid_t[numForks]; // fire off as many children as we need (this is one less than the parent since the parent will run as well) for (int i = 0; i < numForks; i++) { processID = fork(); if (processID == 0) { - // this is one of the children, break so we don't start a fork bomb + // this is in one of the children, break so we don't start a fork bomb break; + } else { + // this is in the parent, save the ID of the forked process + childForks[i] = processID; } } } if (processID == 0 || numForks == 0) { - // this is one of the child forks or there is a single assignment client, continue assignment-client execution - - // create a NodeList as an unassigned client - NodeList* nodeList = NodeList::createInstance(NODE_TYPE_UNASSIGNED); - - // set the custom assignment socket if we have it - if (customAssignmentSocket.sin_addr.s_addr != 0) { - nodeList->setAssignmentServerSocket((sockaddr*) &customAssignmentSocket); - } - - // change the timeout on the nodelist socket to be as often as we want to re-request - nodeList->getNodeSocket()->setBlockingReceiveTimeoutInUsecs(ASSIGNMENT_REQUEST_INTERVAL_USECS); - - timeval lastRequest = {}; - - unsigned char packetData[MAX_PACKET_SIZE]; - ssize_t receivedBytes = 0; - - // grab the assignment pool from argv, if it was passed - const char* ASSIGNMENT_POOL_PARAMETER = "-p"; - const char* assignmentPool = getCmdOption(argc, argv, ASSIGNMENT_POOL_PARAMETER); - - // create a request assignment, accept all assignments, pass the desired pool (if it exists) - Assignment requestAssignment(Assignment::Request, Assignment::All, assignmentPool); - - while (true) { - if (usecTimestampNow() - usecTimestamp(&lastRequest) >= ASSIGNMENT_REQUEST_INTERVAL_USECS) { - gettimeofday(&lastRequest, NULL); - // if we're here we have no assignment, so send a request - nodeList->sendAssignment(requestAssignment); - } - - if (nodeList->getNodeSocket()->receive(packetData, &receivedBytes) && - packetData[0] == PACKET_TYPE_DEPLOY_ASSIGNMENT && packetVersionMatch(packetData)) { - - // construct the deployed assignment from the packet data - Assignment deployedAssignment(packetData, receivedBytes); - - qDebug() << "Received an assignment - " << deployedAssignment << "\n"; - - // switch our nodelist DOMAIN_IP to the ip receieved in the assignment - if (deployedAssignment.getDomainSocket()->sa_family == AF_INET) { - in_addr domainSocketAddr = ((sockaddr_in*) deployedAssignment.getDomainSocket())->sin_addr; - nodeList->setDomainIP(inet_ntoa(domainSocketAddr)); - - qDebug() << "Changed domain IP to " << inet_ntoa(domainSocketAddr); - } - - if (deployedAssignment.getType() == Assignment::AudioMixer) { - AudioMixer::run(); - } else { - AvatarMixer::run(); - } - - qDebug() << "Assignment finished or never started - waiting for new assignment"; - - // reset our NodeList by switching back to unassigned and clearing the list - nodeList->setOwnerType(NODE_TYPE_UNASSIGNED); - nodeList->clear(); - } - } + childClient(); } else { - // don't bail until all children have finished - wait(NULL); + parentMonitor(); } } \ No newline at end of file From 3f941a33715abe16e994ca0e2269bec8c1034152 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Mon, 9 Sep 2013 12:42:18 -0700 Subject: [PATCH 3/3] add a missing include for waitpid --- assignment-client/src/main.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/assignment-client/src/main.cpp b/assignment-client/src/main.cpp index 50e4f069a5..130cca9135 100644 --- a/assignment-client/src/main.cpp +++ b/assignment-client/src/main.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include #include