Ticket #9640: 9640-pari_error_callbacks.patch

File 9640-pari_error_callbacks.patch, 15.9 KB (added by jdemeyer, 8 years ago)

based on 5.12.beta4 + #15124

  • c_lib/include/interrupt.h

    # HG changeset patch
    # User Peter Bruin <P.Bruin@warwick.ac.uk>
    # Date 1379013789 -3600
    # Node ID 582545bce2e8d50611b55354aef098c3fe98affb
    # Parent  081c25f918b63ae48f072f0d74a9722a63efb0cb
    Trac 9640: PARI error catching using callbacks
    
    diff --git a/c_lib/include/interrupt.h b/c_lib/include/interrupt.h
    a b  
    302302    }
    303303}
    304304
     305extern void sage_siglongjmp(int err);
    305306
    306307
    307308/**********************************************************************
  • c_lib/src/interrupt.c

    diff --git a/c_lib/src/interrupt.c b/c_lib/src/interrupt.c
    a b  
    9797#endif
    9898}
    9999
     100inline void sage_siglongjmp(int err) {
     101  reset_CPU();
     102  siglongjmp(_signals.env, err);
     103}
    100104
    101105/* Handler for SIGHUP, SIGINT, SIGALRM */
    102106void sage_interrupt_handler(int sig)
     
    120124            do_raise_exception(sig);
    121125
    122126            /* Jump back to sig_on() (the first one if there is a stack) */
    123             reset_CPU();
    124             siglongjmp(_signals.env, sig);
     127            sage_siglongjmp(sig);
    125128        }
    126129    }
    127130    else
     
    161164        do_raise_exception(sig);
    162165
    163166        /* Jump back to sig_on() (the first one if there is a stack) */
    164         reset_CPU();
    165         siglongjmp(_signals.env, sig);
     167        sage_siglongjmp(sig);
    166168    }
    167169    else
    168170    {
  • module_list.py

    diff --git a/module_list.py b/module_list.py
    a b  
    703703    Extension('sage.libs.pari.gen',
    704704              sources = ["sage/libs/pari/gen.pyx"],
    705705              libraries = ['pari', 'gmp']),
    706    
     706
     707    Extension('sage.libs.pari.pari_error',
     708              sources = ["sage/libs/pari/pari_error.pyx"],
     709              libraries = ['pari', 'gmp']),
     710
    707711    Extension('sage.libs.ppl',
    708712              sources = ['sage/libs/ppl.pyx', 'sage/libs/ppl_shim.cc'],
    709713              libraries = ['ppl', 'gmpxx', 'gmp', 'm'],
  • sage/libs/pari/decl.pxi

    diff --git a/sage/libs/pari/decl.pxi b/sage/libs/pari/decl.pxi
    a b  
    4747    ctypedef unsigned long pari_sp
    4848    extern pari_sp avma, bot, top
    4949
     50    # parierr.h
     51
     52    int talker2, bugparier, alarmer, openfiler, talker, flagerr, impl, \
     53        archer, notfuncer, precer, typeer, consister, user, errpile, \
     54        overflower, matinv1, mattype1, arither1, primer1, invmoder, \
     55        constpoler, notpoler, redpoler, zeropoler, operi, operf, gdiver, \
     56        memer, negexper, sqrter5, noer
     57
     58    int warner, warnprec, warnfile, warnmem
    5059
    5160    # parigen.h
    5261
     
    112121    GEN     gnil
    113122    extern int INIT_JMPm, INIT_SIGm, INIT_DFTm
    114123    extern int new_galois_format, precdl
     124    extern int (*cb_pari_handle_exception)(long)
     125    extern void (*cb_pari_err_recover)(long)
    115126
    116127    # level1.h
    117128   
     
    10871098    void    pari_flush()
    10881099    void    pari_putc(char c)
    10891100    void    pari_puts(char *s)
     1101    int     pari_last_was_newline()
     1102    void    pari_set_last_newline(int last)
    10901103    #void    print(GEN g)   # syntax error
    10911104    void    print1(GEN g)
    10921105    void    printtex(GEN g)
     
    19091922        void (*puts)(char*)
    19101923        void (*flush)()
    19111924    extern PariOUT* pariOut
     1925    extern PariOUT* pariErr
    19121926
    19131927
    19141928cdef extern from 'pari/paripriv.h':
  • sage/libs/pari/gen.pxd

    diff --git a/sage/libs/pari/gen.pxd b/sage/libs/pari/gen.pxd
    a b  
    33cimport sage.structure.element
    44cimport sage.structure.parent_base
    55
    6 cdef void _pari_trap "_pari_trap"(long errno, long retries) except *
    7 
    86cdef class gen(sage.structure.element.RingElement):
    97    cdef GEN g
    108    cdef object _refers_to
     
    2018
    2119cdef class PariInstance(sage.structure.parent_base.ParentWithBase):
    2220    cdef gen PARI_ZERO, PARI_ONE, PARI_TWO
     21    cdef str error_string
    2322    cdef gen new_gen(self, GEN x)
    2423    cdef object new_gen_to_string(self, GEN x)
    2524    cdef gen new_gen_noclear(self, GEN x)
  • sage/libs/pari/gen.pyx

    diff --git a/sage/libs/pari/gen.pyx b/sage/libs/pari/gen.pyx
    a b  
    92469246    str[0] = c
    92479247    str[1] = 0
    92489248    sys.stdout.write(str)
     9249    # Let PARI think the last character was a newline,
     9250    # so it doesn't print one when an error occurs.
     9251    pari_set_last_newline(1)
    92499252    return
    92509253
    92519254cdef void sage_puts(char* s):
    92529255    sys.stdout.write(s)
     9256    pari_set_last_newline(1)
    92539257    return
    92549258
    92559259cdef void sage_flush():
    92569260    sys.stdout.flush()
    92579261    return
    92589262
     9263cdef PariOUT sage_pariErr
     9264
     9265cdef void sage_pariErr_putchar(char c):
     9266    cdef char s[2]
     9267    s[0] = c
     9268    s[1] = 0
     9269    pari_instance.error_string += str(s)
     9270
     9271cdef void sage_pariErr_puts(char *s):
     9272    pari_instance.error_string += str(s)
     9273
     9274cdef void sage_pariErr_flush():
     9275    pass
     9276
    92599277
    92609278cdef class PariInstance(sage.structure.parent_base.ParentWithBase):
    92619279    def __init__(self, long size=16000000, unsigned long maxprime=500000):
     
    93029320        global num_primes, avma, top, bot, prec
    93039321
    93049322        # The size here doesn't really matter, because we will allocate
    9305         # our own stack anyway. We ask PARI not to set up signal handlers.
    9306         pari_init_opts(10000, maxprime, INIT_JMPm | INIT_DFTm)
     9323        # our own stack anyway. We ask PARI not to set up signal and
     9324        # error handlers.
     9325        pari_init_opts(10000, maxprime, INIT_DFTm)
     9326
     9327        _pari_init_error_handling(self)
     9328
    93079329        num_primes = maxprime
    93089330
    9309         # NOTE: pari_catch_sig_on() can only come AFTER pari_init_opts()!
    9310         pari_catch_sig_on()
    9311 
    93129331        # Free the PARI stack and allocate our own (using Cython)
    93139332        pari_free(<void*>bot); bot = 0
    93149333        init_stack(size)
     
    93229341        GP_DATA.fmt.sigd = prec_bits_to_dec(53)
    93239342
    93249343        # Set printing functions
    9325         global pariOut
     9344        global pariOut, pariErr
     9345
    93269346        pariOut = &sage_pariOut
    93279347        pariOut.putch = sage_putchar
    93289348        pariOut.puts = sage_puts
    93299349        pariOut.flush = sage_flush
    9330         pari_catch_sig_off()
     9350
     9351        pariErr = &sage_pariErr
     9352        pariErr.putch = sage_pariErr_putchar
     9353        pariErr.puts = sage_pariErr_puts
     9354        pariErr.flush = sage_pariErr_flush
    93319355
    93329356    def __dealloc__(self):
    93339357        """
     
    97839807            return v
    97849808
    97859809        t = str(s)
    9786         pari_catch_sig_str('evaluating PARI string')
     9810        pari_catch_sig_on()
    97879811        g = gp_read_str(t)
    97889812        if g == gnil:
    97899813            pari_catch_sig_off()
     
    1052710551
    1052810552cdef extern from "pari/pari.h":
    1052910553    char *errmessage[]
    10530     int talker2, bugparier, alarmer, openfiler, talker, flagerr, impl, \
    10531         archer, notfuncer, precer, typeer, consister, user, errpile, \
    10532         overflower, matinv1, mattype1, arither1, primer1, invmoder, \
    10533         constpoler, notpoler, redpoler, zeropoler, operi, operf, gdiver, \
    10534         memer, negexper, sqrter5, noer
    10535     int warner, warnprec, warnfile, warnmem
    1053610554
    1053710555cdef extern from "misc.h":
    1053810556    int     factorint_withproof_sage(GEN* ans, GEN x, GEN cutoff)
     
    1056910587        """
    1057010588        return self.args[0]
    1057110589
     10590    def errtext(self):
     10591        """
     10592        Return the message output by PARI when this error occurred.
     10593
     10594        EXAMPLE::
     10595
     10596            sage: try:
     10597            ....:     pari('pi()')
     10598            ....: except PariError, e:
     10599            ....:     print e.errtext()
     10600            ....:
     10601              ***   at top-level: pi()
     10602              ***                 ^----
     10603              ***   not a function in function call
     10604
     10605        """
     10606        return self.args[1]
     10607
    1057210608    def __repr__(self):
    1057310609        r"""
    1057410610        TESTS::
     
    1059110627        return "%s (%d)"%(self.errmessage(self.errnum()), self.errnum())
    1059210628
    1059310629
    10594 # We expose a trap function to C.
    10595 # If this function returns without raising an exception,
    10596 # the code is retried.
    10597 # This is a proof-of-concept example.
    10598 # THE TRY CODE IS NOT REENTRANT -- NO CALLS TO PARI FROM HERE !!!
    10599 #              - Gonzalo Tornario
    10600 
    10601 cdef void _pari_trap "_pari_trap"(long errno, long retries) except *:
    10602     if retries > 100:
    10603         pari_catch_sig_off()
    10604         raise RuntimeError("_pari_trap recursion too deep")
    10605     if errno == errpile:
    10606         P.allocatemem(silent=True)
    10607     elif errno == user:
    10608         pari_catch_sig_off()
    10609         raise RuntimeError("PARI user exception")
    10610     else:
    10611         pari_catch_sig_off()
    10612         raise PariError(errno)
    10613 
    10614 
    1061510630def vecsmall_to_intlist(gen v):
    1061610631    """
    1061710632    INPUT:
  • sage/libs/pari/pari_err.h

    diff --git a/sage/libs/pari/pari_err.h b/sage/libs/pari/pari_err.h
    a b  
    55/*****************************************
    66   Interrupts and PARI exception handling
    77 *****************************************/
    8 #define pari_catch_sig_on() sig_on(); _pari_catch;
    9 #define pari_catch_sig_str(s) sig_str(s); _pari_catch;
    10 #define pari_catch_sig_off() _pari_endcatch; sig_off();
    118
     9static void (*_pari_check_warning)(void);
     10static int (*_pari_handle_exception)(long err);
    1211
    13 // global catch variable !
    14 // this means that the code is not reentrant -- beware !
    15 // THAT MEANS NO CALLING TO PARI from inside the trap....
    16 // Should replace by a stack, that would work.
    17 static volatile long sage_pari_catcherr = 0;
     12#define _pari_sig_on_(message) (unlikely(_pari_sig_on_prejmp(message, __FILE__, __LINE__))    \
     13                                || _sig_on_postjmp(sigsetjmp(_signals.env, 0)))
    1814
    19 /* Careful with pari_retries, it must be declared volatile!
    20  * Also note that "pari_errno = setjmp(...)" is not legal C.
    21  */
    22 #define _pari_catch                                                           \
    23     jmp_buf _pari_catch_env;                                                  \
    24     {                                                                         \
    25         volatile long pari_retries = 0;                                       \
    26         sage_pari_catcherr = 0;                                               \
    27         long pari_errno = setjmp(_pari_catch_env);                            \
    28         if (pari_errno) {                                                     \
    29             _pari_trap(pari_errno, pari_retries);                             \
    30             if(PyErr_Occurred()) {                                            \
    31                 _pari_endcatch;                                               \
    32                 return NULL;                                                  \
    33             }                                                                 \
    34             pari_retries++;                                                   \
    35         }                                                                     \
    36         sage_pari_catcherr = err_catch(CATCH_ALL, &_pari_catch_env);          \
    37     }
     15static inline int _pari_sig_on_prejmp(const char* message, const char* file, int line) {
     16  int i = _sig_on_prejmp(message, file, line);
     17  if (!i)
     18    cb_pari_handle_exception = _pari_handle_exception;
     19  return i;
     20}
    3821
    39 #define _pari_endcatch {                                                      \
    40          err_leave(sage_pari_catcherr);                                       \
    41     }
     22static inline void _pari_sig_off_(const char *file, int line) {
     23  _sig_off_(file, line);
     24  if(_signals.sig_on_count == 0
     25     && cb_pari_handle_exception != NULL) {
     26    /*
     27      No PARI error was raised, but we may have to print
     28      pending warning messages.
     29    */
     30    (*_pari_check_warning)();
     31    cb_pari_handle_exception = NULL;
     32  }
     33}
    4234
     35#define pari_catch_sig_on() _pari_sig_on_(NULL)
     36#define pari_catch_sig_str(s) _pari_sig_on_(s)
     37#define pari_catch_sig_off() _pari_sig_off_(__FILE__, __LINE__)
  • sage/libs/pari/pari_err.pxi

    diff --git a/sage/libs/pari/pari_err.pxi b/sage/libs/pari/pari_err.pxi
    a b  
    1 # We don't need anything from here, as we have PARI-specific signal
    2 # handling.  We still include this such that Cython detects the
    3 # dependency on interrupt.h for recompiling gen.pyx.
     1# Let Cython know that pari_err.h includes interrupt.h
    42cdef extern from 'interrupt.h':
    53    pass
    64
     
    97    int pari_catch_sig_str(char *) except 0
    108    void pari_catch_sig_off()
    119
    12 from sage.libs.pari.gen cimport _pari_trap
     10from sage.libs.pari.pari_error cimport (_pari_init_error_handling, _pari_check_warning,
     11                                        _pari_handle_exception, _pari_err_recover)
  • new file sage/libs/pari/pari_error.pxd

    diff --git a/sage/libs/pari/pari_error.pxd b/sage/libs/pari/pari_error.pxd
    new file mode 100644
    - +  
     1from sage.libs.pari.gen cimport PariInstance
     2
     3cdef void _pari_init_error_handling "_pari_init_error_handling" (PariInstance P)
     4cdef void _pari_check_warning "_pari_check_warning" ()
     5cdef int _pari_handle_exception "_pari_handle_exception" (long err)
     6cdef void _pari_err_recover "_pari_err_recover" (long err)
  • new file sage/libs/pari/pari_error.pyx

    diff --git a/sage/libs/pari/pari_error.pyx b/sage/libs/pari/pari_error.pyx
    new file mode 100644
    - +  
     1"""
     2Functions for handling PARI errors
     3
     4AUTHORS:
     5
     6- Peter Bruin (September 2013): initial version
     7
     8"""
     9include "sage/ext/stdsage.pxi"
     10include "sage/ext/interrupt.pxi"
     11include "sage/ext/python.pxi"
     12include "decl.pxi"
     13
     14cdef extern from "<signal.h>":
     15    int SIGUSR1
     16
     17cdef extern from "interrupt.h":
     18    void sage_siglongjmp(int err)
     19
     20from sage.libs.pari.gen cimport PariInstance
     21cdef PariInstance pari
     22
     23cdef void _pari_init_error_handling(PariInstance P):
     24    """
     25    Set up our code for handling PARI errors.
     26
     27    TEST::
     28
     29        sage: try:
     30        ....:     p = pari.polcyclo(-1)
     31        ....: except PariError, e:
     32        ....:     print e.errtext()
     33        ....:
     34          ***   argument must be positive in polcyclo.
     35
     36    """
     37    P.error_string = ""
     38    global pari
     39    pari = P
     40    global cb_pari_err_recover
     41    cb_pari_err_recover = _pari_err_recover
     42
     43cdef void _pari_check_warning():
     44    """
     45    Print pending PARI library warning messages to stderr.
     46
     47    TEST::
     48
     49    sage: pari('warning("test")')
     50      ***   user warning: test
     51
     52    """
     53    if pari.error_string != "":
     54        import sys
     55        sys.stderr.write(pari.error_string)
     56        pari.error_string = ""
     57
     58
     59# The following two functions are called by the function pari_err() in
     60# the PARI library.
     61
     62cdef int _pari_handle_exception(long err):
     63    """
     64    Convert a PARI error into a Sage exception.
     65
     66    EXAMPLES::
     67
     68        sage: pari('error("test")')
     69        Traceback (most recent call last):
     70        ...
     71        RuntimeError: PARI user exception
     72          ***   at top-level: error("test")
     73          ***                 ^-------------
     74          ***   user error: test
     75
     76        sage: pari(1)/pari(0)
     77        Traceback (most recent call last):
     78        ...
     79        PariError: division by zero (27)
     80
     81    """
     82    if err == errpile:
     83        pari.allocatemem(silent=True)
     84    elif err == user:
     85        PyErr_SetObject(RuntimeError, "PARI user exception\n%s" % pari.error_string)
     86    else:
     87        from sage.libs.pari.gen import PariError
     88        PyErr_SetObject(PariError, (err, pari.error_string))
     89    return 0
     90
     91cdef void _pari_err_recover(long err):
     92    """
     93    Reset the error string and perform a longjmp().
     94
     95    The longjmp() is done either via sig_retry(), which indicates that
     96    the code should be retried, or with a positive value indicating
     97    that an error occurred.
     98
     99    TEST:
     100
     101    Perform a computation that requires doubling the default stack
     102    several times::
     103
     104        sage: from sage.libs.pari.gen import init_pari_stack
     105        sage: init_pari_stack(2^12)
     106        sage: x = pari('2^(2^26)')
     107        sage: x == 2^(2^26)
     108        True
     109
     110    """
     111    pari.error_string = ""
     112    if (err == errpile) and not PyErr_Occurred():
     113        # We successfully enlarged the PARI stack.
     114        sig_retry()
     115    else:
     116        # An exception was raised.  We disable PARI error catching and
     117        # call sage_siglongjmp() with a more or less arbitrary value;
     118        # the only important thing is that it is positive.
     119        global cb_pari_handle_exception
     120        cb_pari_handle_exception = NULL
     121        sage_siglongjmp(SIGUSR1)
     122    # not reached