From a1fd34ebb621b5e2fedeaec568456598cfc158e9 Mon Sep 17 00:00:00 2001 From: Dhanya Maliakal Date: Thu, 13 Jul 2017 12:18:45 +0200 Subject: [PATCH] for users ensured proper destructors, and ctrl c should kill it --- users/mainReceiver.cpp | 226 +++++++++++++++++++++++------------------ users/userReceiver | Bin 13808 -> 23906 bytes 2 files changed, 128 insertions(+), 98 deletions(-) diff --git a/users/mainReceiver.cpp b/users/mainReceiver.cpp index f93a6674f..b97eb4874 100644 --- a/users/mainReceiver.cpp +++ b/users/mainReceiver.cpp @@ -6,12 +6,15 @@ #include #include -#include //SIGINT +#include //SIGINT #include //system #include "utilities.h" #include "logger.h" +#include //wait +#include //wait + #include using namespace std; @@ -19,35 +22,35 @@ using namespace std; #define START_TCP_PORT 1954 #define PRINT_IN_COLOR(c,f, ...) printf ("\033[%dm" f RESET, 30 + c+1, ##__VA_ARGS__) -slsReceiverUsers *receivers[NUM_RECEIVERS]; -void deleteReceiver(slsReceiverUsers* r[]){ - for (int i = 0; i < NUM_RECEIVERS; ++i) - if (r[i]) { - delete r[i]; - r[i] = 0; - } -} +pid_t childPid[NUM_RECEIVERS]; +bool keeprunning; +int numrunning; -void closeFile(int p){ - deleteReceiver(receivers); + + +void sigChildExitedHandler(int sig) { + pid_t pid = wait(NULL); + bprintf(GRAY, "\nChild Process Pid %d exited.\n", pid); + numrunning--; } - +void sigInterruptHandler(int p){ + keeprunning = false; +} int StartAcq(char* filepath, char* filename, uint64_t fileindex, uint32_t datasize, void*p){ - printf("--StartAcq: filepath:%s filename:%s fileindex:%llu datasize:%u\n", + bprintf(BLUE, "#### StartAcq: filepath:%s filename:%s fileindex:%llu datasize:%u ####\n", filepath, filename, fileindex, datasize); - printf("--StartAcq: returning 0\n"); + bprintf(BLUE, "--StartAcq: returning 0\n"); return 0; } void AcquisitionFinished(uint64_t frames, void*p){ - printf("AcquisitionFinished: frames:%llu \n",frames); - + bprintf(BLUE, "#### AcquisitionFinished: frames:%llu ####\n",frames); } @@ -56,7 +59,7 @@ void GetData(uint64_t frameNumber, uint32_t expLength, uint32_t packetNumber, ui char* datapointer, uint32_t datasize, void* p){ PRINT_IN_COLOR (xCoord, - "%d GetData: \n" + "#### %d GetData: ####\n" "frameNumber: %llu\t\texpLength: %u\t\tpacketNumber: %u\t\tbunchId: %llu\t\ttimestamp: %llu\t\tmodId: %u\t\t" "xCoord: %u\t\tyCoord: %u\t\tzCoord: %u\t\tdebug: %u\t\troundRNumber: %u\t\tdetType: %u\t\t" "version: %u\t\tfirstbytedata: 0x%x\t\tdatsize: %u\n\n", @@ -70,104 +73,131 @@ void GetData(uint64_t frameNumber, uint32_t expLength, uint32_t packetNumber, ui int main(int argc, char *argv[]) { - //Catch signal SIGINT to close files properly - signal(SIGINT,closeFile); + // set default child process pid values + for (int i = 0; i < NUM_RECEIVERS; ++i) + childPid[i] = -1; + + keeprunning = true; + numrunning = 0; + + // Catch signal SIGINT to close files and call destructors properly + struct sigaction sa; + sa.sa_flags=0; // no flags + sa.sa_handler=sigInterruptHandler; // handler function + sigemptyset(&sa.sa_mask); // dont block additional signals during invocation of handler + if (sigaction(SIGINT, &sa, NULL) == -1) { + bprintf(RED, "Could not set handler function for SIGINT\n"); + } + + // wait for all the SIGCHILD signals + struct sigaction asa; + asa.sa_flags=0; // no flags + asa.sa_handler=sigChildExitedHandler; // handler function + sigemptyset(&asa.sa_mask); // dont block additional signals during invocation of handler + if (sigaction(SIGCHLD, &asa, NULL) == -1) { + bprintf(RED, "Could not set handler function for SICHILD\n"); + } + - int ret = slsReceiverDefs::OK; int narg= 3; for (int i = 0; i < NUM_RECEIVERS; ++i) { - char temp[10]; - sprintf(temp,"%d",START_TCP_PORT + i); - char* args[] = {(char*)"ignored", (char*)"--rx_tcpport", temp}; + childPid[i] = fork(); - cprintf(BLUE,"Starting Receiver %d\n", i); - receivers[i] = new slsReceiverUsers(narg, args, ret); - if(ret==slsReceiverDefs::FAIL){ - deleteReceiver(receivers); - return -1; + // fork failed + if (childPid[i] < 0) { + bprintf(RED,"fork() failed. Killing all the receiver objects\n"); + raise(SIGINT); } - //register callbacks - //remember to set file write enable to 0 (using the client) if we should not write files and - //you will write data using the callbacks + // child process + else if (childPid[i] == 0) { + bprintf(BLUE,"Starting Receiver %d with pid %ld\n", i, (long)getpid()); - /** - * Call back for start acquisition - * callback arguments are - * filepath - * filename - * fileindex - * datasize - * - * return value is insignificant at the moment - * we write depending on file write enable - * users get data to write depending on call backs registered - */ - printf("Registering StartAcq()\n"); - receivers[i]->registerCallBackStartAcquisition(StartAcq, NULL); + char temp[10]; + sprintf(temp,"%d",START_TCP_PORT + i); + char* args[] = {(char*)"ignored", (char*)"--rx_tcpport", temp}; + int ret = slsReceiverDefs::OK; + slsReceiverUsers *receiver = new slsReceiverUsers(narg, args, ret); + if(ret==slsReceiverDefs::FAIL){ + delete receiver; + exit(EXIT_FAILURE); + } - /** - * Call back for acquisition finished - * callback argument is - * total frames caught - */ - printf("Registering AcquisitionFinished()\n"); - receivers[i]->registerCallBackAcquisitionFinished(AcquisitionFinished, NULL); + //register callbacks + //remember to set file write enable to 0 (using the client) if we should not write files and + //you will write data using the callbacks - /** - * Call back for raw data - * args to raw data ready callback are - * frameNumber is the frame number - * expLength is the subframe number (32 bit eiger) or real time exposure time in 100ns (others) - * packetNumber is the packet number - * bunchId is the bunch id from beamline - * timestamp is the time stamp with 10 MHz clock - * modId is the unique module id (unique even for left, right, top, bottom) - * xCoord is the x coordinate in the complete detector system - * yCoord is the y coordinate in the complete detector system - * zCoord is the z coordinate in the complete detector system - * debug is for debugging purposes - * roundRNumber is the round robin set number - * detType is the detector type see :: detectorType - * version is the version number of this structure format - * dataPointer is the pointer to the data - * dataSize in bytes is the size of the data in bytes - */ - printf("Registering GetData() \n"); - receivers[i]->registerCallBackRawDataReady(GetData,NULL); + /** + * Call back for start acquisition + * callback arguments are + * filepath + * filename + * fileindex + * datasize + * + * return value is insignificant at the moment + * we write depending on file write enable + * users get data to write depending on call backs registered + */ + bprintf(BLUE, "Registering StartAcq()\n"); + receiver->registerCallBackStartAcquisition(StartAcq, NULL); + + /** + * Call back for acquisition finished + * callback argument is + * total frames caught + */ + bprintf(BLUE, "Registering AcquisitionFinished()\n"); + receiver->registerCallBackAcquisitionFinished(AcquisitionFinished, NULL); + + /** + * Call back for raw data + * args to raw data ready callback are + * frameNumber is the frame number + * expLength is the subframe number (32 bit eiger) or real time exposure time in 100ns (others) + * packetNumber is the packet number + * bunchId is the bunch id from beamline + * timestamp is the time stamp with 10 MHz clock + * modId is the unique module id (unique even for left, right, top, bottom) + * xCoord is the x coordinate in the complete detector system + * yCoord is the y coordinate in the complete detector system + * zCoord is the z coordinate in the complete detector system + * debug is for debugging purposes + * roundRNumber is the round robin set number + * detType is the detector type see :: detectorType + * version is the version number of this structure format + * dataPointer is the pointer to the data + * dataSize in bytes is the size of the data in bytes + */ + bprintf(BLUE, "Registering GetData() \n"); + receiver->registerCallBackRawDataReady(GetData,NULL); - //start tcp server thread - ret = receivers[i]->start(); - if(ret == slsReceiverDefs::FAIL){ - for (int i = 0; i < i; ++i) - receivers[i]->stop(); - deleteReceiver(receivers); - return -1; + //start tcp server thread + if (receiver->start() == slsReceiverDefs::FAIL){ + delete receiver; + exit(EXIT_FAILURE); + } + + while(keeprunning); + delete receiver; + exit(EXIT_SUCCESS); } + + // parent process + else + numrunning++; + } + FILE_LOG(logINFO) << "Ready ... "; + bprintf(GRAY, "\n[ Press \'Ctrl+c\' to exit ]\n"); - FILE_LOG(logDEBUG1) << "DONE!" << endl; - cprintf( BLUE, "Type \'q\' to exit\n"); - string str; - cin>>str; - //wait and look for an exit keyword - while(str.find("q") == string::npos) - cin>>str; - //stop tcp server thread, stop udp socket - for (int i = 0; i < NUM_RECEIVERS; ++i) { - cprintf(BLUE,"Stopping Receiver %d\n",i); - receivers[i]->stop(); - } - - - deleteReceiver(receivers); - - - + // wait for all child processes to exit + while(numrunning); + FILE_LOG(logINFO) << "Goodbye!"; return 0; } diff --git a/users/userReceiver b/users/userReceiver index f67dd6d6893a0fbb2dd3225f8743c5969df6ccdf..d3f1f835eb32c8e33ecd57e4e54d41d2a5eecd9e 100755 GIT binary patch literal 23906 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 U1GshuRBAAqwMkd|dyhW+KcZnKMF0Q* literal 13808 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