450 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			C
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			450 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 <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 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.
 | 
						|
 */
 | 
						|
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) {
 | 
						|
  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;
 | 
						|
}
 |