| | 611 | /// Base exception for time measure functions |
| | 612 | class TimeMeasureException : public Exception { |
| | 613 | public: |
| | 614 | /// Constructor |
| | 615 | TimeMeasureException() throw() {} |
| | 616 | /// Virtual destructor |
| | 617 | virtual ~TimeMeasureException() throw() {} |
| | 618 | ///A short description of the exception |
| | 619 | virtual const char* what() const throw() { |
| | 620 | return "lemon::TimeMeasureException"; |
| | 621 | } |
| | 622 | }; |
| | 623 | |
| | 624 | /// Exception thrown when time measuring fails |
| | 625 | class TimeMeasureError : public TimeMeasureException { |
| | 626 | public: |
| | 627 | /// Constructor |
| | 628 | TimeMeasureError() throw() {} |
| | 629 | /// Virtual destructor |
| | 630 | virtual ~TimeMeasureError() throw() {} |
| | 631 | ///A short description of the exception |
| | 632 | virtual const char* what() const throw() { |
| | 633 | return "lemon::TimeMeasureError"; |
| | 634 | } |
| | 635 | }; |
| | 636 | |
| | 637 | /// Exception thrown when the executed function times out |
| | 638 | class TimeoutException : public TimeMeasureException { |
| | 639 | public: |
| | 640 | /// Constructor |
| | 641 | TimeoutException() throw() {} |
| | 642 | /// Virtual destructor |
| | 643 | virtual ~TimeoutException() throw() {} |
| | 644 | ///A short description of the exception |
| | 645 | virtual const char* what() const throw() { |
| | 646 | return "lemon::TimeoutException"; |
| | 647 | } |
| | 648 | }; |
| | 649 | |
| | 650 | /// Exception thrown when the executed function throws an exception |
| | 651 | class ExecutionException : public TimeMeasureException { |
| | 652 | private: |
| | 653 | std::string _message; |
| | 654 | mutable std::string _what; |
| | 655 | public: |
| | 656 | /// Constructor |
| | 657 | ExecutionException(const char* message, std::size_t len) throw() { |
| | 658 | try { |
| | 659 | _message.clear(); |
| | 660 | _message.assign(message, len); |
| | 661 | } catch (...) {} |
| | 662 | } |
| | 663 | /// Virtual destructor |
| | 664 | virtual ~ExecutionException() throw() {} |
| | 665 | ///A short description of the exception |
| | 666 | virtual const char* what() const throw() { |
| | 667 | try { |
| | 668 | _what.clear(); |
| | 669 | std::ostringstream oss; |
| | 670 | oss << "lemon::ExecutionException: " << _message; |
| | 671 | _what = oss.str(); |
| | 672 | return _what.c_str(); |
| | 673 | } catch (...) {} |
| | 674 | return "lemon::ExecutionException"; |
| | 675 | } |
| | 676 | }; |
| | 677 | |
| | 678 | namespace _time_measure_bits { |
| | 679 | #ifndef LEMON_WIN32 |
| | 680 | inline bool write_full(int fd, const void* buffer, std::size_t len) { |
| | 681 | std::size_t offset = 0; |
| | 682 | while (offset < len) { |
| | 683 | ssize_t ret = write(fd, static_cast<const char*>(buffer) + offset, len - offset); |
| | 684 | if (ret < 0) { |
| | 685 | return false; |
| | 686 | } |
| | 687 | offset += ret; |
| | 688 | } |
| | 689 | return true; |
| | 690 | } |
| | 691 | |
| | 692 | inline bool read_full(int fd, void* buffer, std::size_t len) { |
| | 693 | std::size_t offset = 0; |
| | 694 | while (offset < len) { |
| | 695 | ssize_t ret = read(fd, static_cast<char*>(buffer) + offset, len - offset); |
| | 696 | if (ret < 0) { |
| | 697 | return false; |
| | 698 | } |
| | 699 | offset += ret; |
| | 700 | } |
| | 701 | return true; |
| | 702 | } |
| | 703 | #endif |
| | 704 | } |
| | 705 | |
| | 706 | /// Tool to measure the running time with a time constraint. |
| | 707 | |
| | 708 | /// This function executes \c f on a forked subprocess, and if the subprocess |
| | 709 | /// does not terminate in the given time constraint then it is killed by the |
| | 710 | /// master process. Because \c f is executed in a subprocess, the side-effects |
| | 711 | /// are not affecting the master process. |
| | 712 | /// |
| | 713 | /// Because WIN32 API does not have efficient \c fork() implementation, this |
| | 714 | /// function is not implemented for Windows. |
| | 715 | |
| | 716 | /// \param f the function object to be measured. |
| | 717 | /// \param max_time the maximum total running time of \c f in seconds. |
| | 718 | ///\return The running time of \c f reported by the subprocess. |
| | 719 | template<class F> |
| | 720 | TimeStamp constrainedRunningTimeTest(F f, double max_time) |
| | 721 | { |
| | 722 | #ifndef LEMON_WIN32 |
| | 723 | int pipefd[2]; |
| | 724 | if (pipe(pipefd) == -1) { |
| | 725 | throw TimeMeasureError(); |
| | 726 | } |
| | 727 | |
| | 728 | pid_t cpid = fork(); |
| | 729 | if (cpid == -1) { |
| | 730 | close(pipefd[0]); |
| | 731 | close(pipefd[1]); |
| | 732 | throw TimeMeasureError(); |
| | 733 | } |
| | 734 | |
| | 735 | if (cpid == 0) { |
| | 736 | close(pipefd[0]); |
| | 737 | try { |
| | 738 | Timer t; |
| | 739 | f(); |
| | 740 | TimeStamp ts = t; |
| | 741 | bool success = true; |
| | 742 | _time_measure_bits::write_full(pipefd[1], &success, sizeof(success)); |
| | 743 | _time_measure_bits::write_full(pipefd[1], &ts, sizeof(ts)); |
| | 744 | } catch (const std::exception& e) { |
| | 745 | const char* message = e.what(); |
| | 746 | size_t len = strlen(message); |
| | 747 | bool success = false; |
| | 748 | _time_measure_bits::write_full(pipefd[1], &success, sizeof(success)); |
| | 749 | _time_measure_bits::write_full(pipefd[1], &len, sizeof(len)); |
| | 750 | _time_measure_bits::write_full(pipefd[1], message, len); |
| | 751 | } catch (...) { |
| | 752 | static const char* message = "Unknown exception"; |
| | 753 | size_t len = strlen(message); |
| | 754 | bool success = false; |
| | 755 | _time_measure_bits::write_full(pipefd[1], &success, sizeof(bool)); |
| | 756 | _time_measure_bits::write_full(pipefd[1], &len, sizeof(len)); |
| | 757 | _time_measure_bits::write_full(pipefd[1], message, len); |
| | 758 | } |
| | 759 | close(pipefd[1]); |
| | 760 | _exit(0); |
| | 761 | } else { |
| | 762 | close(pipefd[1]); |
| | 763 | |
| | 764 | fd_set rfds; |
| | 765 | FD_ZERO(&rfds); |
| | 766 | FD_SET(pipefd[0], &rfds); |
| | 767 | |
| | 768 | double sec_int, sec_frac; |
| | 769 | sec_int = modf(max_time, &sec_frac); |
| | 770 | struct timeval tv; |
| | 771 | tv.tv_sec = sec_int; |
| | 772 | tv.tv_usec = sec_frac * 1000000; |
| | 773 | |
| | 774 | int sel = select(pipefd[0] + 1, &rfds, NULL, NULL, &tv); |
| | 775 | |
| | 776 | if (sel < -1) { |
| | 777 | close(pipefd[0]); |
| | 778 | kill(cpid, SIGKILL); |
| | 779 | throw TimeMeasureError(); |
| | 780 | } |
| | 781 | |
| | 782 | if (sel == 0) { |
| | 783 | close(pipefd[0]); |
| | 784 | kill(cpid, SIGKILL); |
| | 785 | throw TimeoutException(); |
| | 786 | } else { |
| | 787 | bool success = false; |
| | 788 | _time_measure_bits::read_full(pipefd[0], &success, sizeof(bool)); |
| | 789 | if (success) { |
| | 790 | TimeStamp ts; |
| | 791 | _time_measure_bits::read_full(pipefd[0], &ts, sizeof(ts)); |
| | 792 | close(pipefd[0]); |
| | 793 | return ts; |
| | 794 | } else { |
| | 795 | size_t len; |
| | 796 | _time_measure_bits::read_full(pipefd[0], &len, sizeof(len)); |
| | 797 | std::vector<char> buffer(len); |
| | 798 | _time_measure_bits::read_full(pipefd[0], buffer.data(), len); |
| | 799 | throw ExecutionException(buffer.data(), len); |
| | 800 | } |
| | 801 | } |
| | 802 | } |
| | 803 | #else |
| | 804 | throw TimeMeasureError(); |
| | 805 | #endif |
| | 806 | } |
| | 807 | |
| | 808 | |