pmm  1.0.0
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros
pmm_main.c
Go to the documentation of this file.
1 /*
2  Copyright (C) 2008-2010 Robert Higgins
3  Author: Robert Higgins <robert.higgins@ucd.ie>
4 
5  This file is part of PMM.
6 
7  PMM is free software: you can redistribute it and/or modify
8  it under the terms of the GNU General Public License as published by
9  the Free Software Foundation, either version 3 of the License, or
10  (at your option) any later version.
11 
12  PMM is distributed in the hope that it will be useful,
13  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  GNU General Public License for more details.
16 
17  You should have received a copy of the GNU General Public License
18  along with PMM. If not, see <http://www.gnu.org/licenses/>.
19 */
20 /*!
21  * @file pmm_main.c
22  * @brief The pmm application
23  *
24  * Main function for the pmm application
25  */
26 #if HAVE_CONFIG_H
27 #include "config.h"
28 #endif
29 
30 #include <stdio.h> // for fileno,freopen
31 #include <stdlib.h> // for free, exit
32 #include <signal.h> // for signal,sigwait,sigfillset,pthread_sigmask,kill
33 #include <sys/signal.h> // for signal
34 #include <pthread.h> // for pthreads
35 // TODO platform specific code follows, need to fix this
36 #include <unistd.h> // for fork,setsid,getpid
37 #include <sys/time.h>
38 #include <time.h> // for nanosleep
39 #include <paths.h>
40 #include <sys/types.h> // for kill,umask,getpid
41 #include <sys/stat.h> // for umask
42 //#include <fcntl.h>
43 
44 #include "pmm_model.h"
45 #include "pmm_load.h"
46 #include "pmm_loadmonitor.h"
47 #include "pmm_argparser.h"
48 #include "pmm_cfgparser.h"
49 #include "pmm_scheduler.h"
50 #include "pmm_executor.h"
51 #include "pmm_log.h"
52 
53 //global variables
55 pthread_mutex_t executing_benchmark_mutex;
56 
57 int signal_quit = 0;
58 pthread_mutex_t signal_quit_mutex = PTHREAD_MUTEX_INITIALIZER;
59 
60 volatile sig_atomic_t sig_cleanup_received = 0;
61 volatile sig_atomic_t sig_pause_received = 0;
62 volatile sig_atomic_t sig_unpause_received = 0;
63 
64 void run_as_daemon();
65 void sig_cleanup(int sig);
66 void sig_do_nothing();
67 
68 
69 /*******************************************************************************
70  * Daemonize process by forking to the background
71  *
72  */
73 void run_as_daemon() {
74  signal(SIGTERM, SIG_IGN);
75  signal(SIGQUIT, SIG_IGN);
76  signal(SIGHUP, SIG_IGN);
77  signal(SIGINT, SIG_IGN);
78 
79  switch(fork()) {
80  case -1:
81  ERRPRINTF("Error: could not fork to background.\n");
82  exit(EXIT_FAILURE);
83  case 0:
84  break;
85  default:
86  exit(EXIT_SUCCESS);
87  }
88 
89  if(setsid() == (pid_t) -1) {
90  ERRPRINTF("Error: process could not create new session.\n");
91  exit(EXIT_FAILURE);
92  }
93 
94  (void) umask(0);
95 
96 
97 
98  //TODO
99  //close(fileno(stdin));
100  //close(fileno(stdout));
101  //close(fileno(stderr));
102 
103  return;
104 }
105 
106 /*!
107  * single handler thread that should catch signals and set appropriate
108  * gobal variables
109  *
110  * @warning Do not use *PRINTF logging facility in this thread, it is not
111  * 'async-signal-safe' (though it is threadsafe).
112  */
113 void* signal_handler(void* arg)
114 {
115  sigset_t signal_set;
116  int signal;
117 
118  (void)arg; //TODO unused
119 
120  for(;;) {
121 
122  //create a full signal set
123  if(sigfillset(&signal_set) != 0) {
124  ERRPRINTF("Error filling signal_set.\n");
125  exit(EXIT_FAILURE);
126  }
127 
128  //wait for all signals in signal_set
129  sigwait(&signal_set, &signal);
130 
131 
132  LOGPRINTF("signal received: %d\n", signal);
133 
134  // when we get this far, we've caught a signal
135  switch(signal)
136  {
137  // whatever you need to do on SIGQUIT
138  case SIGQUIT:
139  if(pthread_mutex_lock(&signal_quit_mutex) != 0) {
140  ERRPRINTF("Error locking signal_quit_mutex\n");
141  exit(EXIT_FAILURE);
142  }
143 
144  signal_quit = 1;
145 
146  if(pthread_mutex_unlock(&signal_quit_mutex) != 0) {
147  ERRPRINTF("Error unlocking signal_quit_mutex\n");
148  exit(EXIT_FAILURE);
149  }
150 
151  return NULL; //we are done processing signals
152 
153  break;
154  case SIGINT: //do the same with SIGINT
155  if(pthread_mutex_lock(&signal_quit_mutex) != 0) {
156  ERRPRINTF("Error locking signal_quit_mutex\n");
157  exit(EXIT_FAILURE);
158  }
159 
160  signal_quit = 1;
161 
162  if(pthread_mutex_unlock(&signal_quit_mutex) != 0) {
163  ERRPRINTF("Error unlocking signal_quit_mutex\n");
164  exit(EXIT_FAILURE);
165  }
166 
167  return NULL; //we are done processing signals
168 
169  break;
170 
171  // whatever you need to do on SIGINT
172  //case SIGINT:
173  // pthread_mutex_lock(&signal_mutex);
174  // handled_signal = SIGINT;
175  // pthread_mutex_unlock(&signal_mutex);
176  // break;
177 
178  // whatever you need to do for other signals
179  default:
180  break; //nohing
181  }
182  }
183 
184 
185  return NULL;
186 }
187 
188 void sig_cleanup(int sig) {
189  LOGPRINTF("received: %d, cleaning up.\n", sig); //TODO enable this!!!
191 
192  // after we receive the sigint we are going to catch but do nothing with
193  // further sigints, namely the one that will be sent to the load monitoring
194  // thread
195  signal(SIGINT, sig_do_nothing);
196 }
197 
199  return;
200 }
201 
202 void redirect_output(char* logfile) {
203  FILE *f;
204 
205  f = freopen(logfile, "a", stdout);
206  if(f == NULL) {
207  ERRPRINTF("Error redirecting output to file: %s\n", logfile);
208  exit(EXIT_FAILURE);
209  }
210 }
211 
212 /*!
213  * the main function
214  *
215  * Structure of the main function is as follows:
216  *
217  * - set up signal handlers
218  * - parse command line arguments
219  * - read configuration file, models and load history
220  * - launch load monitoring thread
221  * - enter main loop
222  * - if not executing benchmark
223  * - clean up previous benchmark execution
224  * - pick a new benchmark launch benchmarking thread
225  *
226  * All the while checking for termination signals, handling shutdown and so on.
227  */
228 int main(int argc, char **argv) {
229 
230  struct pmm_config *cfg;
231  struct pmm_routine *scheduled_r = NULL;
232  int scheduled_status = 0;
233 
234  int rc;
235 
236  // benchmark thread variables
237  int b_thread_rc;
238  pthread_t b_thread_id = 0;
239  pthread_attr_t b_thread_attr;
240  void *b_thread_return;
241 
242  // load monitor thread variables
243  int l_thread_rc;
244  pthread_t l_thread_id = 0;
245  pthread_attr_t l_thread_attr;
246 
247  // signal handler thread
248  sigset_t signal_set;
249  int s_thread_rc;
250  pthread_t s_thread_id = 0;
251  pthread_attr_t s_thread_attr;
252 
253  //register Ctrl+C signal handler (undone if we switch to daemon later)
254  //if(signal(SIGINT, sig_cleanup) == SIG_IGN) {
255  // signal(SIGINT, SIG_IGN);
256  //}
257  //
258  //
259 
260  // block all signals
261  sigfillset(&signal_set);
262  pthread_sigmask(SIG_BLOCK, &signal_set, NULL);
263 
264  // create signal handling thread
265  //
266  pthread_attr_init(&s_thread_attr);
267  pthread_attr_setdetachstate(&s_thread_attr, PTHREAD_CREATE_JOINABLE);
268  s_thread_rc = pthread_create(&s_thread_id, &s_thread_attr,
269  signal_handler, NULL);
270  if(s_thread_rc != 0) {
271  ERRPRINTF("Error creating signal handler thread, return code: %d.\n",
272  s_thread_rc);
273  exit(EXIT_FAILURE);
274  }
275 
276 
277  cfg = new_config();
278 
279  // parse arguments
280  parse_args(cfg, argc, argv);
281 
282  xmlparser_init();
283 
284  // read configuation files
285  rc = parse_config(cfg);
286  if(rc < 0) {
287  ERRPRINTF("Error parsing config.\n");
288  exit(EXIT_FAILURE);
289  }
290 
291  // if running as a deamon ...
292  if(cfg->daemon) {
293  LOGPRINTF("running as daemon ...\n");
294  //redirect_output(cfg.logfile);
295  run_as_daemon();
296  }
297 
298 
299  // load models
300  rc = parse_models(cfg);
301  if(rc < 0) {
302  ERRPRINTF("Error loading models.\n");
303  exit(EXIT_FAILURE);
304  }
305 
306  // print configuration
307  print_config(PMM_LOG, cfg);
308 
309 
310  // read previous history
311  rc = parse_history(cfg->loadhistory);
312  if(rc < 0) {
313  if(rc == -1) {
314  //history file empty, start with empty history
315  }
316  else {
317  ERRPRINTF("Error parsing load history.\n");
318  exit(EXIT_FAILURE);
319  }
320  }
321 
322  // launch thread to record load history
323  pthread_rwlock_init(&(cfg->loadhistory->history_rwlock), NULL);
324 
325  pthread_attr_init(&l_thread_attr);
326  pthread_attr_setdetachstate(&l_thread_attr, PTHREAD_CREATE_JOINABLE);
327 
328  LOGPRINTF("Starting load monitor thread.\n");
329  l_thread_rc = pthread_create(&l_thread_id, &l_thread_attr,
330  loadmonitor, (void *)cfg->loadhistory);
331 
332  if(l_thread_rc != 0) {
333  ERRPRINTF("Error creating thread, return code: %d", l_thread_rc);
334  exit(EXIT_FAILURE);
335  }
336 
337 
338  // possibly launch server thread (listens for requests from an cli utility
339  // and from api calls, all over sockets)
340 
341  // initialize some benchmarking variables
343 
344  pthread_attr_init(&b_thread_attr);
345 
346  pthread_attr_setdetachstate(&b_thread_attr, PTHREAD_CREATE_JOINABLE);
347  pthread_mutex_init(&executing_benchmark_mutex, NULL);
348 
349  // main loop
350  for(;;) {
351  //DBGPRINTF("main loop: ...\n");
352 
353  //DBGPRINTF("main loop: locking executing_benchmark.\n");
354  pthread_mutex_lock(&executing_benchmark_mutex);
355 
356  // if no benchmark is executing
357  if(!executing_benchmark) {
358 
359  //DBGPRINTF("main loop: no benchmark executing.\n");
360 
361  // join any previous benchmark thread (should be finished by now)
362  if(b_thread_id != 0) {
363 
364  DBGPRINTF("main loop: Joining previous benchmark thread.\n");
365 
366  b_thread_rc = pthread_join(b_thread_id, &b_thread_return);
367 
368  if(b_thread_rc != 0) {
369  perror("[main]"); //TODO
370  ERRPRINTF("Error joining previous thread.\n");
371  }
372 
373  if(*(int*)b_thread_return == 1) { //quit signal
374  LOGPRINTF("Benchmark quit successful.\n");
375  }
376  else if(*(int*)b_thread_return == -1) { //failure
377  ERRPRINTF("Benchmark failure. Shutting down ...\n");
378  //trigger shutdown
379  kill(getpid(), SIGINT);
380 
381  free(b_thread_return);
382  b_thread_return = NULL;
383 
384  break;
385  }
386 
387  //thread has been finished and cleaned up, reset b_thread_id
388  b_thread_id = 0;
389 
390  free(b_thread_return);
391  b_thread_return = NULL;
392 
393  if(cfg->pause == 1) {
394  printf("Press enter to continue ...");
395  getchar();
396  }
397 
398  }
399 
400  scheduled_status = schedule_routine(&scheduled_r, cfg->routines,
401  cfg->used);
402 
403  DBGPRINTF("schedule status: %i\n", scheduled_status);
404 
405  if(scheduled_status == 0){
406 
407  //if we are only building and not monitor load, terminate
408  //program when all models are built
409  if(cfg->build_only == 1) {
410  LOGPRINTF("All routines completed.\n");
411  kill(getpid(), SIGINT);
412  }
413 
414  //TODO if all models are built we no longer need to attempt
415  //to schedule one this scheduling loop should be terminated
416  }
417  else if(scheduled_status > 1) {
418  DBGPRINTF("Currently no schedule-able routine.\n");
419  }
420  else {
421  DBGPRINTF("main loop: routine picked for execution: %s\n",
422  scheduled_r->name);
423  print_routine(PMM_DBG, scheduled_r);
424 
425 
426  //launch benchmarking thread TODO seperate into function
427  b_thread_rc = pthread_create(&b_thread_id, &b_thread_attr,
428  benchmark, (void *)scheduled_r);
429  if(b_thread_rc != 0) {
430  ERRPRINTF("Error pthread_create: rc:%d\n", b_thread_rc);
431  } else {
432  //thread was created successfully
434  }
435 
436  }
437  //DBGPRINTF("[main]: unlocking executing_benchmark.\n");
438  pthread_mutex_unlock(&executing_benchmark_mutex);
439 
440  }
441  else {
442  //DBGPRINTF("[main]: unlocking executing_benchmark.\n");
443  pthread_mutex_unlock(&executing_benchmark_mutex);
444 
445  //DBGPRINTF("main loop: benchmarking executing, do nothing ...\n");
446 
447  /* here we are going to recheck that the currently executing bench
448  * mark still satisfies the executing conditions and pause/unpause
449  * or cancel as required */
450  }
451 
452  // sleep for a period
453  nanosleep(&(cfg->ts_main_sleep_period), NULL);
454 
455 
456 
457  //check signals
458 
459  // grab the mutex before looking at signal_quit
460  pthread_mutex_lock(&signal_quit_mutex);
461  if(signal_quit) {
462  pthread_mutex_unlock(&signal_quit_mutex);
463  LOGPRINTF("signal_quit set, breaking from main loop ...\n");
464  break;
465  }
466  // remember to release mutex
467  pthread_mutex_unlock(&signal_quit_mutex);
468 
469  }
470 
471  //join benchmark thread, they will see global variable and exit promptly
472  if(b_thread_id != 0)pthread_join(b_thread_id, NULL);
473  pthread_join(l_thread_id, NULL);
474  pthread_join(s_thread_id, NULL);
475 
476  //write models
477  write_models(cfg);
478 
479  pthread_mutex_destroy(&signal_quit_mutex);
480  pthread_mutex_destroy(&executing_benchmark_mutex);
481  pthread_rwlock_destroy(&(cfg->loadhistory->history_rwlock));
482  //pthread_exit(NULL); this allows a thread to continue executing after the
483  //main has finished, don't think we want this here ...
484 
485  //close files
486 
487  //cleanup libxml
489 
490  //free memory
491  free_config(&cfg);
492 
493 
494 }
495