~ubuntu-branches/ubuntu/trusty/nordugrid-arc/trusty

« back to all changes in this revision

Viewing changes to src/hed/libs/common/Run_unix.cpp

  • Committer: Package Import Robot
  • Author(s): Mattias Ellert
  • Date: 2012-12-13 16:41:31 UTC
  • mfrom: (3.1.11 sid)
  • Revision ID: package-import@ubuntu.com-20121213164131-wii0p2fcv7e3en93
Tags: 2.0.1-1
* 2.0.1 Release
* Drop patches accepted upstream

Show diffs side-by-side

added added

removed removed

Lines of Context:
26
26
 
27
27
#include "Run.h"
28
28
 
 
29
#define DUAL_CHECK_LOST_CHILD
29
30
 
30
31
namespace Arc {
31
32
 
 
33
  // This function is hackish workaround for
 
34
  // problem of child processes disappearing without
 
35
  // trace and even waitpid() can't detect that.
 
36
  static bool check_pid(pid_t p, Time& t) {
 
37
    if(p <= 0) {
 
38
      // Child PID is lost - memory corruption?
 
39
      std::cerr<<"HUGE PROBLEM: lost PID of child process: "<<p<<std::endl;
 
40
      return false;
 
41
    }
 
42
    if(::kill(p, 0) != 0) {
 
43
      // ESRCH - child exited and lost
 
44
      // EPERM - PID reused
 
45
      if((errno == ESRCH) || (errno == EPERM)) {
 
46
        // There is slight possibility of race condition
 
47
        // when child already exited but waitpid for it
 
48
        // was not called yet. Let's give code 1 min
 
49
        // to process lost child to be on safe side.
 
50
        if(t.GetTime() != Time::UNDEFINED) {
 
51
          if((Time()-t) > Period(60)) {
 
52
            std::cerr<<"HUGE PROBLEM: lost child process: "<<p<<std::endl;
 
53
            return false;
 
54
          }
 
55
        } else {
 
56
          t = Time();
 
57
        }
 
58
      }
 
59
    }
 
60
    return true;
 
61
  }
 
62
 
32
63
#define SAFE_DISCONNECT(CONNECTOR) { \
33
64
    try { \
34
65
      (CONNECTOR).disconnect(); \
144
175
      thread_ = Glib::Thread::create(sigc::mem_fun(*this, &RunPump::Pump), false);
145
176
    } catch (Glib::Exception& e) {} catch (std::exception& e) {}
146
177
    ;
147
 
    if (thread_ == NULL)
148
 
      return;
 
178
    if (thread_ == NULL) return;
149
179
    // Wait for context_ to be intialized
150
180
    // TODO: what to do if context never initialized
151
181
    for (;;) {
204
234
          };
205
235
        } else {
206
236
          storm_count = 0;
207
 
        };
208
 
      };
 
237
        }
 
238
      }
209
239
    } catch (Glib::Exception& e) {} catch (std::exception& e) {};
210
240
  }
211
241
 
212
242
  void RunPump::Add(Run *r) {
213
 
    if (!r)
214
 
      return;
215
 
    if (!(*r))
216
 
      return;
217
 
    if (!(*this))
218
 
      return;
 
243
    if (!r) return;
 
244
    if (!(*r)) return;
 
245
    if (!(*this)) return;
219
246
    // Take full control over context
220
247
    list_lock_.lock();
221
248
    while (true) {
226
253
    }
227
254
    try {
228
255
      // Add sources to context
229
 
      if (r->stdout_str_ && !(r->stdout_keep_))
 
256
      if (r->stdout_str_ && !(r->stdout_keep_)) {
230
257
        r->stdout_conn_ = context_->signal_io().connect(sigc::mem_fun(*r, &Run::stdout_handler), r->stdout_, Glib::IO_IN | Glib::IO_HUP);
231
 
      if (r->stderr_str_ && !(r->stderr_keep_))
 
258
      }
 
259
      if (r->stderr_str_ && !(r->stderr_keep_)) {
232
260
        r->stderr_conn_ = context_->signal_io().connect(sigc::mem_fun(*r, &Run::stderr_handler), r->stderr_, Glib::IO_IN | Glib::IO_HUP);
233
 
      if (r->stdin_str_ && !(r->stdin_keep_))
 
261
      }
 
262
      if (r->stdin_str_ && !(r->stdin_keep_)) {
234
263
        r->stdin_conn_ = context_->signal_io().connect(sigc::mem_fun(*r, &Run::stdin_handler), r->stdin_, Glib::IO_OUT | Glib::IO_HUP);
 
264
      }
235
265
#ifdef HAVE_GLIBMM_CHILDWATCH
236
266
      r->child_conn_ = context_->signal_child_watch().connect(sigc::mem_fun(*r, &Run::child_handler), r->pid_->pid());
237
267
      //if(r->child_conn_.empty()) std::cerr<<"connect for signal_child_watch failed"<<std::endl;
243
273
  }
244
274
 
245
275
  void RunPump::Remove(Run *r) {
246
 
    if (!r)
247
 
      return;
248
 
    if (!(*r))
249
 
      return;
250
 
    if (!(*this))
251
 
      return;
 
276
    if (!r) return;
 
277
    if (!(*r)) return;
 
278
    if (!(*this)) return;
252
279
    // Take full control over context
253
280
    list_lock_.lock();
254
281
    while (true) {
267
294
      abandoned_.push_back(Abandoned(r->pid_->pid(),context_->signal_child_watch().connect(sigc::mem_fun(*this,&RunPump::child_handler), r->pid_->pid())));
268
295
#endif
269
296
      r->running_ = false;
270
 
    };
 
297
    }
271
298
    pump_lock_.unlock();
272
299
    list_lock_.unlock();
273
300
  }
375
402
                             stderr_keep_ ? NULL : &stderr_);
376
403
      };
377
404
      *pid_ = pid;
378
 
      if (!stdin_keep_)
 
405
      if (!stdin_keep_) {
379
406
        fcntl(stdin_, F_SETFL, fcntl(stdin_, F_GETFL) | O_NONBLOCK);
380
 
      if (!stdout_keep_)
 
407
      }
 
408
      if (!stdout_keep_) {
381
409
        fcntl(stdout_, F_SETFL, fcntl(stdout_, F_GETFL) | O_NONBLOCK);
382
 
      if (!stderr_keep_)
 
410
      }
 
411
      if (!stderr_keep_) {
383
412
        fcntl(stderr_, F_SETFL, fcntl(stderr_, F_GETFL) | O_NONBLOCK);
 
413
      }
384
414
      run_time_ = Time();
385
415
      started_ = true;
386
416
    } catch (Glib::Exception& e) {
405
435
#ifndef HAVE_GLIBMM_CHILDWATCH
406
436
    Wait(0);
407
437
#endif
408
 
    if (!running_)
409
 
      return;
 
438
    if (!running_) return;
410
439
    if (timeout > 0) {
411
440
      // Kill softly
412
441
      ::kill(pid_->pid(), SIGTERM);
413
442
      Wait(timeout);
414
443
    }
415
 
    if (!running_)
416
 
      return;
 
444
    if (!running_) return;
417
445
    // Kill with no merci
418
446
    ::kill(pid_->pid(), SIGKILL);
419
447
  }
434
462
      if ((l == 0) || (l == -1)) {
435
463
        CloseStdout();
436
464
        return false;
437
 
      }
438
 
      else
 
465
      } else {
439
466
        stdout_str_->append(buf, l);
440
 
    }
441
 
    else {
 
467
      }
 
468
    } else {
442
469
      // Event shouldn't happen if not expected
443
470
 
444
471
    }
452
479
      if ((l == 0) || (l == -1)) {
453
480
        CloseStderr();
454
481
        return false;
455
 
      }
456
 
      else
 
482
      } else {
457
483
        stderr_str_->append(buf, l);
458
 
    }
459
 
    else {
 
484
      }
 
485
    } else {
460
486
      // Event shouldn't happen if not expected
461
487
 
462
488
    }
468
494
      if (stdin_str_->length() == 0) {
469
495
        CloseStdin();
470
496
        stdin_str_ = NULL;
471
 
      }
472
 
      else {
 
497
      } else {
473
498
        int l = WriteStdin(0, stdin_str_->c_str(), stdin_str_->length());
474
499
        if (l == -1) {
475
500
          CloseStdin();
476
501
          return false;
477
 
        }
478
 
        else
 
502
        } else {
479
503
          // Not very effective
480
504
          *stdin_str_ = stdin_str_->substr(l);
 
505
        }
481
506
      }
482
 
    }
483
 
    else {
 
507
    } else {
484
508
      // Event shouldn't happen if not expected
485
509
 
486
510
    }
488
512
  }
489
513
 
490
514
  void Run::child_handler(Glib::Pid, int result) {
491
 
    if (stdout_str_)
492
 
      for (;;)
493
 
        if (!stdout_handler(Glib::IO_IN))
494
 
          break;
495
 
    if (stderr_str_)
496
 
      for (;;)
497
 
        if (!stderr_handler(Glib::IO_IN))
498
 
          break;
 
515
    if (stdout_str_) for (;;) if (!stdout_handler(Glib::IO_IN)) break;
 
516
    if (stderr_str_) for (;;) if (!stderr_handler(Glib::IO_IN)) break;
499
517
    //CloseStdout();
500
518
    //CloseStderr();
501
519
    CloseStdin();
505
523
    // as returned by waitpid. It is not clear how it works for
506
524
    // windows but atleast for *nix we can use waitpid related
507
525
    // macros.
 
526
#ifdef DUAL_CHECK_LOST_CHILD
 
527
    if(result == -1) { // special value to indicate lost child
 
528
      result_ = -1;
 
529
    } else
 
530
#endif
508
531
    if(WIFEXITED(result)) {
509
532
      result_ = WEXITSTATUS(result);
510
533
    } else {
513
536
    running_ = false;
514
537
    exit_time_ = Time();
515
538
    lock_.unlock();
516
 
    if (kicker_func_)
517
 
      (*kicker_func_)(kicker_arg_);
 
539
    if (kicker_func_) (*kicker_func_)(kicker_arg_);
518
540
  }
519
541
 
520
542
  void Run::CloseStdout(void) {
521
 
    if (stdout_ != -1)
522
 
      ::close(stdout_);
 
543
    if (stdout_ != -1) ::close(stdout_);
523
544
    stdout_ = -1;
524
545
    SAFE_DISCONNECT(stdout_conn_);
525
546
  }
526
547
 
527
548
  void Run::CloseStderr(void) {
528
 
    if (stderr_ != -1)
529
 
      ::close(stderr_);
 
549
    if (stderr_ != -1) ::close(stderr_);
530
550
    stderr_ = -1;
531
551
    SAFE_DISCONNECT(stderr_conn_);
532
552
  }
533
553
 
534
554
  void Run::CloseStdin(void) {
535
 
    if (stdin_ != -1)
536
 
      ::close(stdin_);
 
555
    if (stdin_ != -1) ::close(stdin_);
537
556
    stdin_ = -1;
538
557
    SAFE_DISCONNECT(stdin_conn_);
539
558
  }
540
559
 
541
560
  int Run::ReadStdout(int timeout, char *buf, int size) {
542
 
    if (stdout_ == -1)
543
 
      return -1;
 
561
    if (stdout_ == -1) return -1;
544
562
    // TODO: do it through context for timeout?
545
563
    for(;;) {
546
564
      pollfd fd;
555
573
  }
556
574
 
557
575
  int Run::ReadStderr(int timeout, char *buf, int size) {
558
 
    if (stderr_ == -1)
559
 
      return -1;
 
576
    if (stderr_ == -1) return -1;
560
577
    // TODO: do it through context for timeout
561
578
    for(;;) {
562
579
      pollfd fd;
571
588
  }
572
589
 
573
590
  int Run::WriteStdin(int timeout, const char *buf, int size) {
574
 
    if (stdin_ == -1)
575
 
      return -1;
 
591
    if (stdin_ == -1) return -1;
576
592
    // TODO: do it through context for timeout
577
593
    for(;;) {
578
594
      pollfd fd;
587
603
  }
588
604
 
589
605
  bool Run::Running(void) {
 
606
#ifdef DUAL_CHECK_LOST_CHILD
 
607
      if(running_) {
 
608
        if(!check_pid(pid_->pid(),exit_time_)) {
 
609
          lock_.unlock();
 
610
          child_handler(pid_->pid(), -1); // simulate exit
 
611
          lock_.lock();
 
612
        }
 
613
      }
 
614
#endif
590
615
#ifdef HAVE_GLIBMM_CHILDWATCH
591
616
    return running_;
592
617
#else
596
621
  }
597
622
 
598
623
  bool Run::Wait(int timeout) {
599
 
    if (!started_)
600
 
      return false;
601
 
    if (!running_)
602
 
      return true;
 
624
    if (!started_) return false;
 
625
    if (!running_) return true;
603
626
    Glib::TimeVal till;
604
627
    till.assign_current_time();
605
628
    till += timeout;
609
632
      t.assign_current_time();
610
633
      t.subtract(till);
611
634
#ifdef HAVE_GLIBMM_CHILDWATCH
612
 
      if (!t.negative())
613
 
        break;
 
635
      if (!t.negative()) break;
614
636
      cond_.timed_wait(lock_, till);
615
637
#else
616
638
      int status;
617
 
      int r = waitpid(pid_->pid(), &status, WNOHANG);
 
639
      int r = ::waitpid(pid_->pid(), &status, WNOHANG);
618
640
      if (r == 0) {
619
 
        if (!t.negative())
620
 
          break;
 
641
        if (!t.negative()) break;
621
642
        lock_.unlock();
622
643
        sleep(1);
623
644
        lock_.lock();
624
645
        continue;
625
646
      }
626
 
      if (r == -1) // Child lost?
627
 
        status = -1;
628
 
      else
629
 
        status = WEXITSTATUS(status);
 
647
      if (r == -1) {
 
648
        // Child lost?
 
649
        status = (-1)<<8;
 
650
      }
630
651
      // Child exited
631
652
      lock_.unlock();
632
 
      child_handler(pid_->pid(), status << 8);
 
653
      child_handler(pid_->pid(), status);
633
654
      lock_.lock();
634
655
#endif
 
656
#ifdef DUAL_CHECK_LOST_CHILD
 
657
      if(running_) {
 
658
        if(!check_pid(pid_->pid(),exit_time_)) {
 
659
          lock_.unlock();
 
660
          child_handler(pid_->pid(), -1); // simulate exit
 
661
          lock_.lock();
 
662
        }
 
663
      }
 
664
#endif
635
665
    }
636
666
    lock_.unlock();
637
667
    return (!running_);
638
668
  }
639
669
 
640
670
  bool Run::Wait(void) {
641
 
    if (!started_)
642
 
      return false;
643
 
    if (!running_)
644
 
      return true;
 
671
    if (!started_) return false;
 
672
    if (!running_) return true;
645
673
    lock_.lock();
646
674
    Glib::TimeVal till;
647
675
    while (running_) {
651
679
      cond_.timed_wait(lock_, till);
652
680
#else
653
681
      int status;
654
 
      int r = waitpid(pid_->pid(), &status, WNOHANG);
 
682
      int r = ::waitpid(pid_->pid(), &status, WNOHANG);
655
683
      if (r == 0) {
656
684
        lock_.unlock();
657
685
        sleep(1);
658
686
        lock_.lock();
659
687
        continue;
660
688
      }
661
 
      if (r == -1) // Child lost?
662
 
        status = -1;
663
 
      else
664
 
        status = WEXITSTATUS(status);
 
689
      if (r == -1) {
 
690
        // Child lost?
 
691
        status = (-1)<<8;
 
692
      }
665
693
      // Child exited
666
694
      lock_.unlock();
667
 
      child_handler(pid_->pid(), status << 8);
 
695
      child_handler(pid_->pid(), status);
668
696
      lock_.lock();
669
697
#endif
 
698
#ifdef DUAL_CHECK_LOST_CHILD
 
699
      if(running_) {
 
700
        if(!check_pid(pid_->pid(),exit_time_)) {
 
701
          lock_.unlock();
 
702
          child_handler(pid_->pid(), -1); // simulate exit
 
703
          lock_.lock();
 
704
        }
 
705
      }
 
706
#endif
670
707
    }
671
708
    lock_.unlock();
672
709
    return (!running_);
689
726
  }
690
727
 
691
728
  void Run::KeepStderr(bool keep) {
692
 
    if (!running_)
693
 
      stderr_keep_ = keep;
 
729
    if (!running_) stderr_keep_ = keep;
694
730
  }
695
731
 
696
732
  void Run::KeepStdin(bool keep) {
697
 
    if (!running_)
698
 
      stdin_keep_ = keep;
 
733
    if (!running_) stdin_keep_ = keep;
699
734
  }
700
735
 
701
736
  void Run::AssignInitializer(void (*initializer_func)(void *arg), void *initializer_arg) {