This repository has been archived on 2023-08-20. You can view files and clone it, but cannot push or open issues or pull requests.
yap-6.3/C/signals.c
2017-05-08 18:51:29 +01:00

452 lines
10 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 <io.h>
#include <stdio.h>
#endif
#include "YapHeap.h"
#include "Yatom.h"
#include "YapEval.h"
#include "yapio.h"
#ifdef TABLING
#include "tab.macros.h"
#endif /* TABLING */
#include <stdio.h>
#include <stdlib.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 */
#if PUSH_REGS
// restore_absmi_regs(&Yap_standard_regs);
#endif
LOCAL_RestartEnv = malloc( sizeof(sigjmp_buf) );
siglongjmp(*LOCAL_RestartEnv, 4);
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 bool 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
}
bool Yap_DisableInterrupts(int wid) {
LOCAL_InterruptsDisabled = true;
YAPEnterCriticalSection();
return true;
}
bool Yap_EnableInterrupts(int wid) {
LOCAL_InterruptsDisabled = false;
YAPLeaveCriticalSection();
return true;
}
/**
Function called to handle delayed interrupts.
*/
bool Yap_HandleSIGINT(void) {
CACHE_REGS
yap_signals sig;
do {
if ((sig = ProcessSIGINT()) != YAP_NO_SIGNAL)
do_signal(worker_id, sig PASS_REGS);
LOCAL_PrologMode &= ~InterruptMode;
return true;
} while (get_signal(YAP_INT_SIGNAL PASS_REGS));
return false;
}
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) {
if (get_signal(YAP_CREEP_SIGNAL PASS_REGS)) {
return Yap_unify(ARG1, TermTrue);
}
return Yap_unify(ARG1, TermFalse);
}
static Int disable_debugging(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 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 continue_signals(USES_REGS1) { return 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", 1, stop_creeping,
NoTracePredFlag | HiddenPredFlag | SafePredFlag);
Yap_InitCPred("$disable_debugging", 0, disable_debugging,
NoTracePredFlag | HiddenPredFlag | SafePredFlag);
Yap_InitCPred("$first_signal", 1, first_signal, SafePredFlag | SyncPredFlag);
Yap_InitCPred("$continue_signals", 0, 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;
}