469 lines
9.9 KiB
C
Executable File
469 lines
9.9 KiB
C
Executable File
/*************************************************************************
|
|
* *
|
|
* YAP Prolog *
|
|
* *
|
|
* Yap Prolog was developed at NCCUP - Universidade do Porto *
|
|
* *
|
|
* Copyright L.Damas, V. Santos Costa and Universidade do Porto 1985-- *
|
|
* *
|
|
**************************************************************************
|
|
* *
|
|
* File: signal.c *
|
|
* comments: Signal Handling & Debugger Support *
|
|
* *
|
|
* *
|
|
* *
|
|
*************************************************************************/
|
|
#ifdef SCCS
|
|
static char SccsId[] = "%W% %G%";
|
|
#endif
|
|
|
|
#define HAS_CACHE_REGS 1
|
|
|
|
#include "Yap.h"
|
|
#if HAVE_UNISTD_H
|
|
#include <unistd.h>
|
|
#endif
|
|
#if _WIN32
|
|
#include <stdio.h>
|
|
#include <io.h>
|
|
#endif
|
|
#include "Yatom.h"
|
|
#include "YapHeap.h"
|
|
#include "eval.h"
|
|
#include "yapio.h"
|
|
#ifdef TABLING
|
|
#include "tab.macros.h"
|
|
#endif /* TABLING */
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#if HAVE_STRING_H
|
|
#include <string.h>
|
|
#endif
|
|
#if HAVE_STRINGS_H
|
|
#include <strings.h>
|
|
#endif
|
|
#if HAVE_MALLOC_H
|
|
#include <malloc.h>
|
|
#endif
|
|
#include <wchar.h>
|
|
#ifdef LOW_LEVEL_TRACER
|
|
#include <tracer.h>
|
|
#endif
|
|
|
|
|
|
/*
|
|
* The InteractSIGINT function is called after a normal interrupt had been caught.
|
|
* It allows 6 possibilities: abort, continue, trace, debug, help, exit.
|
|
*/
|
|
static yap_signals
|
|
InteractSIGINT(int ch) {
|
|
#ifdef HAVE_SETBUF
|
|
/* make sure we are not waiting for the end of line */
|
|
setbuf (stdin, NULL);
|
|
#endif
|
|
switch (ch) {
|
|
case 'a':
|
|
/* abort computation */
|
|
return YAP_ABORT_SIGNAL;
|
|
case 'b':
|
|
/* continue */
|
|
return YAP_BREAK_SIGNAL;
|
|
case 'c':
|
|
/* continue */
|
|
return YAP_NO_SIGNAL;
|
|
case 'd':
|
|
/* enter debug mode */
|
|
return YAP_DEBUG_SIGNAL;
|
|
case 'e':
|
|
/* exit */
|
|
Yap_exit(1);
|
|
return YAP_EXIT_SIGNAL;
|
|
case 'g':
|
|
/* stack dump */
|
|
return YAP_STACK_DUMP_SIGNAL;
|
|
case 't':
|
|
/* start tracing */
|
|
return YAP_TRACE_SIGNAL;
|
|
#ifdef LOW_LEVEL_TRACER
|
|
case 'T':
|
|
toggle_low_level_trace();
|
|
return YAP_NO_SIGNAL;
|
|
#endif
|
|
case 's':
|
|
/* show some statistics */
|
|
return YAP_STATISTICS_SIGNAL;
|
|
case EOF:
|
|
return YAP_NO_SIGNAL;
|
|
case 'h':
|
|
case '?':
|
|
default:
|
|
/* show an helpful message */
|
|
fprintf(stderr, "Please press one of:\n");
|
|
fprintf(stderr, " a for abort\n c for continue\n d for debug\n");
|
|
fprintf(stderr, " e for exit\n g for stack dump\n s for statistics\n t for trace\n");
|
|
fprintf(stderr, " b for break\n");
|
|
return YAP_NO_SIGNAL;
|
|
}
|
|
}
|
|
|
|
/*
|
|
This function talks to the user about a signal. We assume we are in
|
|
the context of the main Prolog thread (trivial in Unix, but hard in WIN32)
|
|
*/
|
|
static yap_signals
|
|
ProcessSIGINT(void)
|
|
{
|
|
CACHE_REGS
|
|
int ch, out;
|
|
#if _WIN32
|
|
if (!_isatty(0)) {
|
|
return YAP_INT_SIGNAL;
|
|
}
|
|
#elif HAVE_ISATTY
|
|
if (!isatty(0)) {
|
|
return YAP_INT_SIGNAL;
|
|
}
|
|
#endif
|
|
LOCAL_PrologMode |= AsyncIntMode;
|
|
do {
|
|
ch = Yap_GetCharForSIGINT();
|
|
} while (!(out = InteractSIGINT(ch)));
|
|
LOCAL_PrologMode &= ~AsyncIntMode;
|
|
return(out);
|
|
}
|
|
|
|
inline static void
|
|
do_signal(int wid, yap_signals sig USES_REGS)
|
|
{
|
|
#if THREADS
|
|
__sync_fetch_and_or ( &REMOTE(wid)->Signals_, SIGNAL_TO_BIT(sig));
|
|
if (!REMOTE_InterruptsDisabled(wid)) {
|
|
REMOTE_ThreadHandle(wid).current_yaam_regs->CreepFlag_ =
|
|
Unsigned(REMOTE_ThreadHandle(wid).current_yaam_regs->LCL0_);
|
|
}
|
|
#else
|
|
LOCAL_Signals |= SIGNAL_TO_BIT(sig);
|
|
if (!LOCAL_InterruptsDisabled) {
|
|
CreepFlag =
|
|
Unsigned(LCL0);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
|
|
|
|
inline static int
|
|
get_signal(yap_signals sig USES_REGS)
|
|
{
|
|
#if THREADS
|
|
uint64_t old;
|
|
|
|
// first, clear the Creep Flag, now if someone sets it it is their problem
|
|
CalculateStackGap( PASS_REGS1 );
|
|
// reset the flag
|
|
if ( (old =__sync_fetch_and_and( &LOCAL_Signals, ~SIGNAL_TO_BIT(sig) ) ) !=
|
|
SIGNAL_TO_BIT(sig)) {
|
|
if (!LOCAL_InterruptsDisabled && LOCAL_Signals != 0) {
|
|
CreepFlag = (CELL)LCL0;
|
|
}
|
|
if (!(old & SIGNAL_TO_BIT(sig)) ) {
|
|
// not there?
|
|
return FALSE;
|
|
}
|
|
// more likely case, we have other interrupts.
|
|
return TRUE;
|
|
}
|
|
// success, we are good
|
|
return TRUE;
|
|
// should we set the flag?
|
|
#else
|
|
if (LOCAL_Signals & SIGNAL_TO_BIT(sig)) {
|
|
LOCAL_Signals &= ~SIGNAL_TO_BIT(sig);
|
|
if (!LOCAL_InterruptsDisabled && LOCAL_Signals != 0) {
|
|
CreepFlag = (CELL)LCL0;
|
|
} else {
|
|
CalculateStackGap( PASS_REGS1 );
|
|
}
|
|
return TRUE;
|
|
} else {
|
|
return FALSE;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/**
|
|
Function called to handle delayed interrupts.
|
|
*/
|
|
int
|
|
Yap_HandleInterrupts( void )
|
|
{
|
|
CACHE_REGS
|
|
yap_signals sig;
|
|
|
|
if ( get_signal( YAP_INT_SIGNAL PASS_REGS )) {
|
|
if ( (sig = ProcessSIGINT()) != YAP_NO_SIGNAL )
|
|
do_signal(worker_id, sig PASS_REGS);
|
|
LOCAL_PrologMode &= ~InterruptMode;
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static Int
|
|
p_creep( USES_REGS1 )
|
|
{
|
|
Atom at;
|
|
PredEntry *pred;
|
|
|
|
at = AtomCreep;
|
|
pred = RepPredProp(PredPropByFunc(Yap_MkFunctor(at, 1),0));
|
|
CreepCode = pred;
|
|
do_signal(worker_id, YAP_CREEP_SIGNAL PASS_REGS);
|
|
return TRUE;
|
|
}
|
|
|
|
static Int
|
|
p_creep_fail( USES_REGS1 )
|
|
{
|
|
Atom at;
|
|
PredEntry *pred;
|
|
|
|
at = AtomCreep;
|
|
pred = RepPredProp(PredPropByFunc(Yap_MkFunctor(at, 1),0));
|
|
CreepCode = pred;
|
|
do_signal(worker_id, YAP_CREEP_SIGNAL PASS_REGS);
|
|
return FALSE;
|
|
}
|
|
|
|
static Int stop_creeping( USES_REGS1 )
|
|
{
|
|
get_signal( YAP_CREEP_SIGNAL PASS_REGS );
|
|
return true;
|
|
}
|
|
|
|
static Int
|
|
creep_allowed( USES_REGS1 )
|
|
{
|
|
if (PP != NULL) {
|
|
get_signal(YAP_CREEP_SIGNAL PASS_REGS);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void
|
|
Yap_signal(yap_signals sig)
|
|
{
|
|
CACHE_REGS
|
|
do_signal(worker_id, sig PASS_REGS);
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
static Int
|
|
p_debug( USES_REGS1 );
|
|
#endif
|
|
|
|
void
|
|
Yap_external_signal(int wid, yap_signals sig)
|
|
{
|
|
#if THREADS
|
|
REGSTORE *regcache = REMOTE_ThreadHandle(wid).current_yaam_regs;
|
|
#endif
|
|
do_signal(wid, sig PASS_REGS);
|
|
LOCAL_PrologMode &= ~InterruptMode;
|
|
}
|
|
|
|
int
|
|
Yap_get_signal__(yap_signals sig USES_REGS)
|
|
{
|
|
return get_signal(sig PASS_REGS);
|
|
}
|
|
|
|
// the caller holds the lock.
|
|
int
|
|
Yap_has_signals__(yap_signals sig1, yap_signals sig2 USES_REGS)
|
|
{
|
|
return LOCAL_Signals & (SIGNAL_TO_BIT(sig1)|SIGNAL_TO_BIT(sig2));
|
|
}
|
|
|
|
|
|
int
|
|
Yap_only_has_signals__(yap_signals sig1, yap_signals sig2 USES_REGS)
|
|
{
|
|
uint64_t sigs = LOCAL_Signals;
|
|
return sigs & (SIGNAL_TO_BIT(sig1) | SIGNAL_TO_BIT(sig2)) &&
|
|
! (sigs & ~(SIGNAL_TO_BIT(sig1) | SIGNAL_TO_BIT(sig2))) ;
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
|
|
volatile int volat = 0;
|
|
|
|
static Int
|
|
p_debug( USES_REGS1 )
|
|
{ /* $debug(+Flag) */
|
|
int i = IntOfTerm(Deref(ARG1));
|
|
while (volat == 0) {
|
|
}
|
|
if (i >= 'a' && i <= 'z')
|
|
GLOBAL_Option[i - 96] = !GLOBAL_Option[i - 96];
|
|
return (1);
|
|
}
|
|
void Yap_loop(void);
|
|
void Yap_debug_end_loop(void);
|
|
|
|
void Yap_loop(void)
|
|
{
|
|
while (volat == 0);
|
|
}
|
|
|
|
void Yap_debug_end_loop(void)
|
|
{
|
|
volat = 1;
|
|
}
|
|
#endif
|
|
|
|
static Int
|
|
p_first_signal( USES_REGS1 )
|
|
{
|
|
Atom at;
|
|
yap_signals sig;
|
|
|
|
while (TRUE) {
|
|
uint64_t mask = LOCAL_Signals;
|
|
if (mask == 0)
|
|
return FALSE;
|
|
#if HAVE___BUILTIN_FFSLL
|
|
sig = __builtin_ffsll(mask);
|
|
#elif HAVE_FFSLL
|
|
sig = ffsll(mask);
|
|
#else
|
|
sig = Yap_msb( mask PASS_REGS)+1;
|
|
#endif
|
|
if (get_signal(sig PASS_REGS)) {
|
|
break;
|
|
}
|
|
}
|
|
loop:
|
|
switch (sig) {
|
|
case YAP_INT_SIGNAL:
|
|
sig = ProcessSIGINT();
|
|
if (sig == YAP_INT_SIGNAL) {
|
|
at = AtomSigInt;
|
|
break;
|
|
}
|
|
if (sig != YAP_NO_SIGNAL)
|
|
goto loop;
|
|
return FALSE;
|
|
case YAP_ABORT_SIGNAL:
|
|
/* abort computation */
|
|
LOCAL_PrologMode &= ~AsyncIntMode;
|
|
if (LOCAL_PrologMode & (GCMode|ConsoleGetcMode|CritMode)) {
|
|
LOCAL_PrologMode |= AbortMode;
|
|
return -1;
|
|
} else {
|
|
Yap_Error(ABORT_EVENT, TermNil, "abort from console");
|
|
}
|
|
Yap_RestartYap( 1 );
|
|
return FALSE;
|
|
case YAP_CREEP_SIGNAL:
|
|
at = AtomSigCreep;
|
|
break;
|
|
case YAP_TRACE_SIGNAL:
|
|
at = AtomSigTrace;
|
|
break;
|
|
case YAP_DEBUG_SIGNAL:
|
|
at = AtomSigDebug;
|
|
break;
|
|
case YAP_BREAK_SIGNAL:
|
|
at = AtomSigBreak;
|
|
break;
|
|
case YAP_FAIL_SIGNAL:
|
|
at = AtomFail;
|
|
break;
|
|
case YAP_STACK_DUMP_SIGNAL:
|
|
at = AtomSigStackDump;
|
|
break;
|
|
case YAP_STATISTICS_SIGNAL:
|
|
at = AtomSigStatistics;
|
|
break;
|
|
#ifdef SIGALRM
|
|
case YAP_ALARM_SIGNAL:
|
|
#endif
|
|
case YAP_WINTIMER_SIGNAL:
|
|
at = AtomSigAlarm;
|
|
break;
|
|
#ifdef SIGVTALRM
|
|
case YAP_VTALARM_SIGNAL:
|
|
at = AtomSigVTAlarm;
|
|
break;
|
|
#endif
|
|
case YAP_EXIT_SIGNAL:
|
|
Yap_exit(1);
|
|
return FALSE;
|
|
case YAP_WAKEUP_SIGNAL:
|
|
at = AtomSigWakeUp;
|
|
break;
|
|
case YAP_ITI_SIGNAL:
|
|
at = AtomSigIti;
|
|
break;
|
|
#ifdef SIGPIPE
|
|
case YAP_PIPE_SIGNAL:
|
|
at = AtomSigPipe;
|
|
break;
|
|
#endif
|
|
#ifdef SIGHUP
|
|
case YAP_HUP_SIGNAL:
|
|
at = AtomSigHup;
|
|
break;
|
|
#endif
|
|
#ifdef SIGUSR1
|
|
case YAP_USR1_SIGNAL:
|
|
at = AtomSigUsr1;
|
|
break;
|
|
#endif
|
|
#ifdef SIGUSR2
|
|
case YAP_USR2_SIGNAL:
|
|
at = AtomSigUsr2;
|
|
break;
|
|
#endif
|
|
#ifdef SIGFPE
|
|
case YAP_FPE_SIGNAL:
|
|
at = AtomSigFPE;
|
|
break;
|
|
#endif
|
|
default:
|
|
return FALSE;
|
|
}
|
|
return Yap_unify(ARG1, MkAtomTerm(at));
|
|
}
|
|
|
|
static Int
|
|
p_continue_signals( USES_REGS1 )
|
|
{
|
|
return p_first_signal( PASS_REGS1 );
|
|
}
|
|
|
|
void
|
|
Yap_InitSignalCPreds(void)
|
|
{
|
|
/* Basic predicates for the debugger */
|
|
Yap_InitCPred("$creep", 0, p_creep, SafePredFlag);
|
|
Yap_InitCPred("$creep_fail", 0, p_creep_fail, SafePredFlag);
|
|
Yap_InitCPred("$stop_creeping", 0, stop_creeping, NoTracePredFlag|HiddenPredFlag|SafePredFlag);
|
|
Yap_InitCPred("$disable_debugging", 0, stop_creeping, NoTracePredFlag|HiddenPredFlag|SafePredFlag);
|
|
Yap_InitCPred ("$first_signal", 1, p_first_signal, SafePredFlag|SyncPredFlag);
|
|
Yap_InitCPred ("$continue_signals", 0, p_continue_signals, SafePredFlag|SyncPredFlag);
|
|
Yap_InitCPred("creep_allowed", 0, creep_allowed, 0);
|
|
#ifdef DEBUG
|
|
Yap_InitCPred("sys_debug", 1, p_debug, SafePredFlag|SyncPredFlag);
|
|
#endif
|
|
}
|
|
|
|
void *Yap_InitSignals(int wid)
|
|
{
|
|
void *ptr = (void *)malloc(sizeof(UInt)*REMOTE_MaxActiveSignals(wid));
|
|
return ptr;
|
|
}
|