Line data Source code
1 : /*******************************************************************************
2 : * *
3 : * Cosmos:(C)oncept et (O)utils (S)tatistique pour les (Mo)deles *
4 : * (S)tochastiques *
5 : * *
6 : * Copyright (C) 2009-2012 LSV & LACL *
7 : * Authors: Paolo Ballarini BenoƮt Barbot & Hilal Djafri *
8 : * Website: http://www.lsv.ens-cachan.fr/Software/cosmos *
9 : * *
10 : * This program is free software; you can redistribute it and/or modify *
11 : * it under the terms of the GNU General Public License as published by *
12 : * the Free Software Foundation; either version 3 of the License, or *
13 : * (at your option) any later version. *
14 : * *
15 : * This program is distributed in the hope that it will be useful, *
16 : * but WITHOUT ANY WARRANTY; without even the implied warranty of *
17 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
18 : * GNU General Public License for more details. *
19 : * *
20 : * You should have received a copy of the GNU General Public License along *
21 : * with this program; if not, write to the Free Software Foundation, Inc., *
22 : * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. *
23 : *******************************************************************************
24 : */
25 :
26 : /**
27 : * \file server.cpp
28 : * This file contain the implementation of a server waiting for
29 : * The simulators to give him result and aggregate them.
30 : * Most of this file is C code.
31 : */
32 :
33 :
34 : #include <string>
35 : #include <stdlib.h>
36 : #include <stdio.h>
37 : #include <sstream>
38 : #include <algorithm>
39 : #include <iostream>
40 : #include <vector>
41 : #include <sys/select.h>
42 : #include <sys/types.h>
43 : #include <sys/ioctl.h>
44 : #include <sys/resource.h>
45 : #include <sys/time.h>
46 : #include <sys/wait.h>
47 : #include <fstream>
48 : #include <unistd.h>
49 : #include <signal.h>
50 : #include <cstdlib>
51 : #include <errno.h>
52 : #include <algorithm>
53 : #include <err.h>
54 : #include <assert.h>
55 : #include <random>
56 :
57 : #include "../Simulator/BatchR.hpp"
58 : #include "result.hpp"
59 : #include "server.hpp"
60 :
61 : using namespace std;
62 :
63 : //! A list of file descriptor for the select system call.
64 : fd_set client_list;
65 :
66 : //! A vector of FILE containing the stdout of each simulator.
67 35 : vector<FILE*> clientstream;
68 :
69 : //! A vector of PID of the simulators
70 35 : vector<pid_t> clientPID;
71 :
72 :
73 :
74 : //! The maximal index of file descriptor in client_list.
75 : int max_client=0 ;
76 :
77 :
78 : //! Boolean indicating if the simulation should continue.
79 : bool continueSelect=false;
80 :
81 0 : void signalHandlerIgn(int){
82 : //cerr << "Receive signal: "<< signum << endl;
83 0 : };
84 :
85 96 : void signalHandler( int signum )
86 : {
87 : //cerr << "Receive signal: "<< signum << endl;
88 96 : switch (signum){
89 : case SIGCHLD: {
90 :
91 : int status;
92 96 : pid_t child = wait(&status);
93 :
94 96 : if(child != -1){
95 6 : if(!WIFEXITED(status)){ //Something to report
96 0 : if(WTERMSIG(status)!=SIGHUP && WTERMSIG(status)!=SIGINT && WTERMSIG(status)!=SIGABRT){ //Normal termination of process
97 0 : if(count(clientPID.begin(),clientPID.end(),child)==0)
98 0 : cerr << "The unknown child "<< child << "Terminated by signal :" << WTERMSIG(status) << endl;
99 : else{
100 0 : if(WIFSIGNALED(status)){
101 0 : cerr << "Simulator "<< child << "Terminated by signal :" << WTERMSIG(status) << endl;
102 0 : exit(EXIT_FAILURE);
103 0 : } else if(WIFEXITED(status)){
104 0 : if(WEXITSTATUS(status) != 130){
105 0 : cout << "Simulator exit with code " << WEXITSTATUS(status) << endl;
106 : }
107 : }else {
108 0 : cerr << "Simulator "<< child << " Crash ! with unknown status "<< status << endl;
109 : }
110 : }
111 : }
112 : }
113 : }
114 96 : break;
115 : }
116 : case SIGINT:
117 0 : continueSelect = false;
118 0 : break;
119 : case SIGPIPE:
120 0 : cerr << "receive a sigpipe" <<endl;
121 0 : break;
122 :
123 :
124 : default:
125 0 : cerr << " Unexpected signal: " << signum << endl;
126 : }
127 96 : }
128 :
129 : /*
130 : * Open a child processes retriving both PID and an a pipe
131 : * to the standart input of the child.
132 : * This function fork and execute the given binary
133 : * on the child.
134 : * @param bin the path to the binary to execute.
135 : * @param argv the list of argument.
136 : */
137 34 : void popenClient(const char* bin, const char *argv[]){
138 34 : pid_t pid = 0;
139 : int pipefd[2];
140 : int pipeerr[2];
141 : FILE* output;
142 :
143 : //create a pipe
144 34 : if(pipe(pipefd) !=0 )err(1,"Fail to launch simulator");
145 34 : if(pipe(pipeerr) != 0)err(1,"Fail to launch simulator");
146 34 : pid = fork(); //fork the process
147 34 : if (pid == 0){
148 : //Child.
149 0 : close(pipefd[0]);
150 0 : close(pipeerr[0]);
151 0 : if(dup2(pipefd[1], STDOUT_FILENO)<0){
152 0 : perror("Fail to redirect stdout");
153 0 : exit(EXIT_FAILURE);
154 : }
155 : //if(dup2(pipeerr[1], STDERR_FILENO)<0)perror("Fail to redirect stderr");
156 0 : if(execvp(bin,(char *const *)argv)<0){
157 0 : perror("Fail to lauch the client");
158 0 : exit(EXIT_FAILURE);
159 : }
160 34 : }else if(pid>0){
161 :
162 : //Only parent gets here. Listen to what the tail says
163 34 : close(pipefd[1]);
164 34 : close(pipeerr[1]);
165 :
166 : //open the output of the client.
167 34 : output= fdopen(pipefd[0], "r");
168 : //if(dup2(STDERR_FILENO,pipeerr[0])<0)perror("Fail to redirect stderr");
169 :
170 34 : clientstream.push_back(output);
171 34 : int streamfd = fileno(output);
172 34 : if(streamfd >max_client)max_client = streamfd;
173 34 : clientPID.push_back(pid);
174 :
175 0 : }else perror("Fail to fork");
176 34 : }
177 :
178 34 : inline void pushint(const char *argv[],size_t &argn,size_t v){
179 34 : char* s = (char *)malloc(255*sizeof(char));
180 34 : sprintf(s, "%zu", v);
181 34 : argv[argn] = s;
182 34 : argn++;
183 34 : }
184 :
185 : inline void pushbool(const char *argv[],size_t &argn,bool v){
186 : char* s = (char *)malloc(255*sizeof(char));
187 : sprintf(s, "%i", v);
188 : argv[argn] = s;
189 : argn++;
190 : }
191 :
192 : inline void pushdouble(const char *argv[],size_t &argn,double v){
193 : char* s = (char *)malloc(255*sizeof(char));
194 : sprintf(s, "%e", v);
195 : argv[argn] = s;
196 : argn++;
197 : }
198 :
199 :
200 68 : inline void pushstr(const char *argv[],size_t &argn,const char* v){
201 68 : char* s = (char *)malloc(255*sizeof(char));
202 68 : sprintf(s, "%s", v);
203 68 : argv[argn] = s;
204 68 : argn++;
205 68 : }
206 :
207 34 : void freestr(const char *argv[],size_t t){
208 136 : for(size_t i =0; i<t;i++)
209 102 : free((void *)argv[i]);
210 34 : }
211 :
212 31 : void launch_clients(parameters& P){
213 :
214 : // if seed is zero generate a pseudo random seed.
215 31 : if(P.seed==0){
216 : timeval t;
217 31 : gettimeofday(&t,(struct timezone*)0);
218 : //os << " " <<(t.tv_usec + t.tv_sec + getpid()+i);
219 62 : std::random_device rd;
220 31 : P.seed = rd();
221 : }
222 :
223 65 : for(int i = 0;i<P.Njob;i++){
224 68 : string cmd = P.tmpPath + "/ClientSim";;
225 34 : const char *argv[15] = {0};
226 34 : size_t argn = 0;
227 34 : pushstr(argv, argn, cmd.c_str());
228 34 : pushstr(argv,argn,P.tmpPath.c_str());
229 34 : pushint(argv,argn,P.seed+i);
230 :
231 34 : if(P.verbose >2){
232 0 : for(size_t i=0; i<argn; i++ )cout << " " << argv[i];
233 0 : cout << endl << endl;
234 : }
235 :
236 34 : popenClient(cmd.c_str(),argv);
237 :
238 :
239 34 : freestr(argv, argn);
240 :
241 : }
242 :
243 31 : }
244 :
245 62 : void kill_client(){
246 : //cout << "Enter kill client" << endl << endl << endl;
247 93 : while (!clientPID.empty()){
248 31 : if (clientPID.back()!=0)kill(clientPID.back(),SIGHUP);
249 : //cerr << "Kill Client " << clientPID.back() << endl;
250 31 : clientstream.pop_back();
251 31 : clientPID.pop_back();
252 : }
253 : //cerr << "Quit kill client" << endl << endl << endl << endl << endl;
254 31 : }
255 :
256 31 : void wait_client(){
257 31 : pid_t child= 1;
258 149 : while (child != -1) {
259 : int termstat;
260 59 : child = wait(&termstat);
261 : }
262 :
263 31 : }
264 :
265 236 : void makeselectlist(void){
266 236 : FD_ZERO(&client_list);
267 490 : for(size_t it = 0;it < clientstream.size(); it++){
268 254 : int fl = fileno(clientstream[it]);
269 254 : FD_SET(fl,&client_list);
270 : }
271 236 : }
272 :
273 : /**
274 : * This function launches a set of simulators and stop them once
275 : * The precision criterion is reach.
276 : */
277 31 : void launchServer(parameters& P){
278 :
279 31 : signal(SIGCHLD , signalHandler);
280 31 : signal(SIGINT, signalHandler);
281 31 : signal(SIGPIPE, signalHandler);
282 :
283 : //Init result
284 62 : result Result;
285 :
286 : //Try to read previous batch
287 31 : if(P.reuse){
288 0 : FILE* intraj = fopen((P.tmpPath+"/saveTraj").c_str(),"r");
289 0 : if(intraj != NULL){
290 0 : BatchR batchResult(P.nbAlgebraic,P.nbQualitatif);
291 0 : batchResult.inputR(intraj);
292 0 : Result.addBatch(batchResult);
293 0 : cout << Result.MeanM2.I << " Previous Trajectories found and loaded" << endl;
294 : }
295 : }
296 :
297 31 : if(P.verbose>0)cout << "START SIMULATION ..." << endl<< endl << endl << endl;
298 :
299 : //Launch a set of simulators
300 31 : launch_clients(P);
301 : //Make a list of file system for polling
302 :
303 : sigset_t blockingset;
304 31 : sigfillset(&blockingset);
305 :
306 31 : continueSelect = true;
307 : //Check if the simulation should continue.
308 503 : while(Result.continueSim() && !clientstream.empty() && continueSelect){
309 236 : makeselectlist();
310 : //wait for a simulator to return some result
311 236 : if(pselect(max_client+1, &client_list, NULL, NULL, NULL,&blockingset) == -1){
312 0 : perror("Server-select() error!");
313 0 : exit(EXIT_FAILURE);
314 : }
315 : //Iterate over the simultor to check wich one has some results.
316 490 : for(size_t it = 0;it < clientstream.size() ;it++){
317 254 : if(FD_ISSET(fileno(clientstream[it]), &client_list)){
318 : //aggregate the new result to the total result
319 472 : BatchR batchResult(P.nbAlgebraic, P.nbQualitatif);
320 236 : if(batchResult.inputR(clientstream[it])){
321 : //batchResult.print();
322 233 : Result.addBatch(batchResult);
323 : //If required output the progress of the computation.
324 233 : if((P.verbose>0 || P.alligatorMode) && P.computeStateSpace==0 )
325 232 : Result.printProgress();
326 : } else {
327 : //The batch result was not complete.
328 : //If the simulator was kill by the server it is OK otherwise
329 : //it is a problem.
330 3 : if(P.verbose>2) cerr << "Warning uncomplete or ill formed Batch Result"<<endl;
331 3 : if(feof( clientstream[it] )!=0){
332 3 : if(P.verbose>2)cerr << "Deconnection Simulator:" << clientPID[it] << endl;
333 3 : clientstream.erase(clientstream.begin() + it);
334 3 : clientPID.erase(clientPID.begin() + it);
335 : }
336 : }
337 : }
338 : }
339 :
340 : }
341 : /*signal(SIGPIPE, signalHandler);*/
342 :
343 : //Kill all the simulator
344 31 : kill_client();
345 :
346 : //Output all the results
347 31 : Result.stopclock();
348 31 : if(P.verbose>0){
349 31 : Result.printCompactResult();
350 31 : cout<< endl;
351 : }
352 :
353 31 : if(P.tmpStatus != 0){
354 62 : ofstream savetraj(P.tmpPath+"/saveTraj");
355 31 : Result.MeanM2.outputR(savetraj);
356 : }
357 :
358 : //use gnuplot
359 31 : if(P.dataPDFCDF.length()>0)Result.outputCDFPDF(P.dataPDFCDF);
360 : //if(P.alligatorMode)
361 31 : signal(SIGCHLD, signalHandlerIgn);
362 31 : Result.printGnuplot();
363 31 : signal(SIGCHLD, signalHandler);
364 :
365 31 : Result.close_gnuplot();
366 :
367 31 : wait_client();
368 :
369 31 : if(P.alligatorMode){
370 0 : Result.printAlligator();
371 : } else{
372 31 : if(P.verbose>1)Result.print(cout);
373 : }
374 :
375 31 : size_t lastslash = P.PathLha.find_last_of("/");
376 31 : if(lastslash==string::npos)lastslash= -1;
377 31 : size_t lastdot = P.PathLha.find_last_of(".");
378 62 : string fn = "Result_" + P.PathLha.substr(lastslash+1,lastdot - lastslash-1 );
379 31 : fn.append(".res");
380 31 : Result.printResultFile(fn);
381 31 : Result.printResultFile("Result.res");
382 :
383 136 : }
|