From 446f8171116c4371cd2a23aa1a7f85bd7ae6f037 Mon Sep 17 00:00:00 2001 From: Olivier Parcollet Date: Mon, 26 May 2014 09:34:22 +0200 Subject: [PATCH] wrapper: add release_GIL_and_enable_signal option. - Add to the wrapper generator (add_method) the release_GIL_and_enable_signal option which : - release the GIL - save the python signal handler - enable the C++ triqs signal handler instead. - undo all of this after the code runs, or in a case of exception. - used python include, ceval.h, line 72 comments and below. - reworked the triqs::signal_handler. simple C like function, no object (no need). start, stop, received, cf header file. - clean the call_back.cpp : only place using the signal directly (qmc uses the callback). in particular, remove the old BOOST CHRONO, since the std::chrono works fine on platforms we use now. --- pytriqs/wrap_generator/wrap_generator.py | 12 ++++- pytriqs/wrap_generator/wrapper.mako.cpp | 22 ++++++++- pytriqs/wrap_test/a.hpp | 14 ++---- pytriqs/wrap_test/g.hpp | 4 ++ pytriqs/wrap_test/my_module_desc.py | 3 ++ triqs/python_tools/wrapper_tools.hpp | 5 +- triqs/utility/callbacks.cpp | 35 ++------------ triqs/utility/exceptions.hpp | 7 +++ triqs/utility/signal_handler.cpp | 61 ++++++++++++------------ triqs/utility/signal_handler.hpp | 36 +++++++------- 10 files changed, 102 insertions(+), 97 deletions(-) diff --git a/pytriqs/wrap_generator/wrap_generator.py b/pytriqs/wrap_generator/wrap_generator.py index fbd61ff1..ac3d5497 100644 --- a/pytriqs/wrap_generator/wrap_generator.py +++ b/pytriqs/wrap_generator/wrap_generator.py @@ -42,8 +42,16 @@ class cfunction : auto result = self_c.method_name(a,b,c). INCOMPATIBLE with c_name. If c_name is given, the default calling_pattern is made. + - release_GIL_and_enable_signal [expert only] : + For long functions in pure C++. + If true the GIL is released in the call of the C++ function and restored after the call. + It also saves the signal handler of python and restore it after the call, + and enable the C++ triqs signal_handler. + This allows e.g. to intercept Ctrl-C during the long C++ function. + The requirement is that the function wrapped must be pure C++, i.e. + no call whatsoever to the python C API, directly or indirectly. """ - def __init__(self, doc = '', is_method = False, no_self_c = False, is_static = False, **kw) : + def __init__(self, doc = '', is_method = False, no_self_c = False, is_static = False, release_GIL_and_enable_signal = False, **kw) : """ Use keywords to build, from the data. Cf doc of class""" self.c_name = kw.pop("c_name", None) self._calling_pattern = kw.pop("calling_pattern", None) @@ -54,6 +62,7 @@ class cfunction : self.doc = doc self.is_method = is_method self.is_static = is_static + self.release_GIL_and_enable_signal = release_GIL_and_enable_signal self.args = [] if 'signature' in kw : assert 'rtype' not in kw and 'args' not in kw, "signature and rtype/args are not compatible" @@ -511,7 +520,6 @@ class module_ : the wrapped_type list """ f = None - print "argv", sys.argv for path in module_path_list : hppfile = path + '/' + modulename + '.hpp' if os.path.exists(hppfile) : diff --git a/pytriqs/wrap_generator/wrapper.mako.cpp b/pytriqs/wrap_generator/wrapper.mako.cpp index 66ec68f2..388e73a4 100644 --- a/pytriqs/wrap_generator/wrapper.mako.cpp +++ b/pytriqs/wrap_generator/wrapper.mako.cpp @@ -16,6 +16,7 @@ using ${ns}; %endfor #include +#include using namespace triqs::py_tools; //--------------------- a dict of python function used in the module but not exposed to user (cf init function) ---------------- @@ -547,7 +548,26 @@ template using self_class = ${self_c_type}; %endif try { + %if overload.release_GIL_and_enable_signal : + PyOS_sighandler_t sig = PyOS_getsig(SIGINT); + triqs::signal_handler::start(); // start the C++ signal handler + Py_BEGIN_ALLOW_THREADS; + try { + %endif ${overload.calling_pattern()}; // the call is here. It sets up "result" : sets up in the python layer. + %if overload.release_GIL_and_enable_signal : + } + catch(...) { + // an error has occurred : clean GIL, handler and rethrow + Py_BLOCK_THREADS; // cf python include, ceval.h, line 72 comments and below. + triqs::signal_handler::stop(); // stop the C++ signal handler + PyOS_setsig(SIGINT, sig); + throw; // + } + Py_END_ALLOW_THREADS; + triqs::signal_handler::stop(); + PyOS_setsig(SIGINT, sig); + %endif %if not overload.is_constructor : %if overload.rtype != "void" : py_result = convert_to_python(std::move(result)); @@ -572,7 +592,7 @@ template } %endif } // end overload ${overload.c_signature()} - % endfor # overload + %endfor # overload %if has_overloads : // finally, no overload was successful. Composing a detailed error message, with the reason of failure of all overload ! diff --git a/pytriqs/wrap_test/a.hpp b/pytriqs/wrap_test/a.hpp index e353765b..b4d9f3dc 100644 --- a/pytriqs/wrap_test/a.hpp +++ b/pytriqs/wrap_test/a.hpp @@ -4,7 +4,7 @@ #include #include //#include - +#include namespace triqs { namespace py_tools { class reductor; class reconstructor; @@ -39,26 +39,18 @@ struct A { } static int sm(int i) { return i*2;} - + int count =0; void long_fnt() { - PyOS_sighandler_t sig = PyOS_getsig(SIGINT); - Py_BEGIN_ALLOW_THREADS; for (int u = 0; u < 101; u +=10) { sleep(1); - if (!triqs::signal_handler().empty()) goto _end; + if (triqs::signal_handler::received(true)) TRIQS_KEYBOARD_INTERRUPT; count = u; std::cout << " inner count " << count << std::endl; } std::cout << " completed" << std::endl; - _end: - - Py_END_ALLOW_THREADS; - PyOS_setsig(SIGINT, sig); - - // PyErr_SetString(PyExc_KeyboardInterrupt, " ended long_fnt"); } double m1(int u) const { diff --git a/pytriqs/wrap_test/g.hpp b/pytriqs/wrap_test/g.hpp index d0b8e12f..5e933e23 100644 --- a/pytriqs/wrap_test/g.hpp +++ b/pytriqs/wrap_test/g.hpp @@ -44,3 +44,7 @@ void pass_sgf(gf_view g) { h5_write(file, "g", g); } } + + + + diff --git a/pytriqs/wrap_test/my_module_desc.py b/pytriqs/wrap_test/my_module_desc.py index e58ded27..56fe9032 100644 --- a/pytriqs/wrap_test/my_module_desc.py +++ b/pytriqs/wrap_test/my_module_desc.py @@ -39,6 +39,9 @@ g.add_method(py_name = "sm", c_name = "sm", signature = "int (int u)", is_static # older syntax, giving rtype and args (better for automatic scripts). g.add_method(py_name = "m1f", c_name = "m1", rtype = "double", doc = "DOC of mm", args = [("int","u"), ("double","y",3)]) +g.add_method(py_name = "long_fnt", c_name = "long_fnt", signature = "void()", release_GIL_and_enable_signal = True) +g.add_member(c_name = "count", c_type = "int",read_only=True) + # add the call operator g.add_call(signature = "int(int u)", doc = "call op") diff --git a/triqs/python_tools/wrapper_tools.hpp b/triqs/python_tools/wrapper_tools.hpp index 0f6151bb..c08fdf22 100644 --- a/triqs/python_tools/wrapper_tools.hpp +++ b/triqs/python_tools/wrapper_tools.hpp @@ -11,7 +11,10 @@ // I can use the trace in triqs::exception #define CATCH_AND_RETURN(MESS,RET)\ -catch(triqs::exception const & e) {\ + catch(triqs::keyboard_interrupt const & e) {\ + PyErr_SetString(PyExc_KeyboardInterrupt, e.what());\ + return RET; }\ + catch(triqs::exception const & e) {\ auto err = std::string("Error " MESS "\nC++ error was : \n") + e.what();\ PyErr_SetString(PyExc_RuntimeError, err.c_str());\ return RET; }\ diff --git a/triqs/utility/callbacks.cpp b/triqs/utility/callbacks.cpp index b56fecdd..96a33655 100644 --- a/triqs/utility/callbacks.cpp +++ b/triqs/utility/callbacks.cpp @@ -18,50 +18,21 @@ * TRIQS. If not, see . * ******************************************************************************/ - #include "callbacks.hpp" #include "signal_handler.hpp" - -// switch to std. Remove old boost code when ok on several compilers -#ifndef TRIQS_USE_BOOST_CHRONO #include namespace triqs { namespace utility { std::function clock_callback(int time_in_seconds) { + signal_handler::start(); if (time_in_seconds <= 0) - return []() { return (!triqs::signal_handler().empty()); }; - - // auto end_time = boost::posix_time::second_clock::local_time() + boost::posix_time::seconds(time_in_seconds); - // return [end_time]() { return (!triqs::signal_handler().empty()) || boost::posix_time::second_clock::local_time() > end_time + return []() { return (!triqs::signal_handler::received()); }; auto end_time = std::chrono::system_clock::now() + std::chrono::seconds(time_in_seconds); - return [end_time]() { return (!triqs::signal_handler().empty()) || (std::chrono::system_clock::now() > end_time); }; + return [end_time]() { return (!triqs::signal_handler::received()) || (std::chrono::system_clock::now() > end_time); }; } } } -#else - -#include -#include - -namespace triqs { -namespace utility { - - bool clock_callback_impl(boost::posix_time::ptime const& end_time) { - return (!triqs::signal_handler().empty()) || boost::posix_time::second_clock::local_time() > end_time; - } - - bool false_callback_impl() { return (!triqs::signal_handler().empty()); } - - std::function clock_callback(int time_in_seconds) { - return (time_in_seconds > 0 ? boost::bind(&clock_callback_impl, boost::posix_time::second_clock::local_time() + - boost::posix_time::seconds(time_in_seconds)) - : std::function(&false_callback_impl)); - } -} -} -#endif - diff --git a/triqs/utility/exceptions.hpp b/triqs/utility/exceptions.hpp index e1e0496d..c117943c 100644 --- a/triqs/utility/exceptions.hpp +++ b/triqs/utility/exceptions.hpp @@ -47,10 +47,17 @@ namespace triqs { template runtime_error & operator <<( T && x) { exception::operator<<(x); return *this; } }; + class keyboard_interrupt : public exception { + public: + keyboard_interrupt() throw() : exception() {} + virtual ~keyboard_interrupt() throw() {} + template keyboard_interrupt & operator <<( T && x) { exception::operator<<(x); return *this; } + }; } #define TRIQS_ERROR(CLASS,NAME) throw CLASS()<<" Triqs "<. * ******************************************************************************/ - #include "signal_handler.hpp" -//#include #include #include -//#include -//#include -// volatile sig_atomic_t signal_handler::keep_going; - - +#include +#include namespace triqs { +namespace signal_handler { - /* - namespace signal_details { - // The signal handler just clears the flag and re-enables itself. - // to replace with settimer if needed - void catch_alarm (int sig) - { - MySignalHandler::keep_going = 0; - signal (sig, Catch_alarm); - } - } - */ - std::vector signal_handler::signals_list; + namespace { - signal_handler::signal_handler() { - static bool initialized; - if (initialized) return; - //keep_going = 1; /* Establish a handler for SIGALRM signals. */ + std::vector signals_list; + bool initialized = false; + + void slot(int signal) { + std::cerr << "TRIQS : Received signal " << signal << std::endl; + signals_list.push_back(signal); + } + } + + void start() { + if (initialized) return; static struct sigaction action; memset(&action, 0, sizeof(action)); - action.sa_handler = &signal_handler::slot; + action.sa_handler = slot; sigaction(SIGINT, &action, NULL); sigaction(SIGTERM, &action, NULL); sigaction(SIGXCPU, &action, NULL); @@ -58,15 +49,23 @@ namespace triqs { sigaction(SIGUSR1, &action, NULL); sigaction(SIGUSR2, &action, NULL); sigaction(SIGSTOP, &action, NULL); - //signal (SIGALRM, _MYSIGNAL::Catch_alarm); - //alarm (0); initialized = true; } - void signal_handler::slot(int signal) { - std::cerr << "Received signal " << signal << std::endl; - signals_list.push_back(signal); + void stop() { + signals_list.clear(); + initialized = false; } + bool received(bool pop_) { + if (!initialized) start(); + bool r = signals_list.size() != 0; + if (r && pop_) pop(); + return r; + } + + int last() { return signals_list.back(); } + void pop() { return signals_list.pop_back(); } +} } diff --git a/triqs/utility/signal_handler.hpp b/triqs/utility/signal_handler.hpp index d671b093..fbe31032 100644 --- a/triqs/utility/signal_handler.hpp +++ b/triqs/utility/signal_handler.hpp @@ -3,7 +3,7 @@ * * TRIQS: a Toolbox for Research in Interacting Quantum Systems * - * Copyright (C) 2011 by M. Ferrero, O. Parcollet + * Copyright (C) 2014 by O. Parcollet * * TRIQS is free software: you can redistribute it and/or modify it under the * terms of the GNU General Public License as published by the Free Software @@ -19,25 +19,23 @@ * TRIQS. If not, see . * ******************************************************************************/ +#pragma once +namespace triqs { +namespace signal_handler { -#ifndef TRIQS_SIGNAL_HANDLER_H -#define TRIQS_SIGNAL_HANDLER_H -#include -#include -#include + /// Start the signal handler + void start(); -namespace triqs { + /// Stop it. ? + void stop(); + + /// A signal has been received. If pop, and there is a signal, pop it. + bool received(bool pop = false); - class signal_handler { - static std::vector signals_list; - //static volatile sig_atomic_t keep_going; - public: - signal_handler(); - bool empty(){ return (signals_list.size() ==0); } - int top(){ return signals_list.back(); } - void pop(){ return signals_list.pop_back(); } - static void slot(int signal); - }; + /// Last received. + int last(); + + /// pop the last signal + void pop(); +} } - -#endif