mirror of
https://github.com/idris-lang/Idris2.git
synced 2024-12-15 14:23:32 +03:00
c4ed1395d9
Operating system counter stores signals as flag set without counter. So sending two signals to a process may result to one or two signal handler invocation. Queueing signals inside Idris could give users false sense of signals being are queue, while they are not. In particular, test for signal could not work reliably for that reason. Also, practically we usually don't need have more than once signal event. This is follow-up to #1660. CC @mattpolzin
159 lines
2.7 KiB
C
159 lines
2.7 KiB
C
#include "idris_signal.h"
|
|
|
|
#include <assert.h>
|
|
#include <errno.h>
|
|
#include <limits.h>
|
|
#include <signal.h>
|
|
#include <stdatomic.h>
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include "idris_util.h"
|
|
|
|
static_assert(ATOMIC_LONG_LOCK_FREE == 2,
|
|
"when not lock free, atomic functions are not async-signal-safe");
|
|
|
|
#define N_SIGNALS 32
|
|
static atomic_uint_fast32_t signals;
|
|
|
|
void _collect_signal(int signum) {
|
|
IDRIS2_VERIFY(signum >= 0 && signum < N_SIGNALS, "signal number out of range: %d", signum);
|
|
|
|
atomic_fetch_or(&signals, (uint32_t) 1 << signum);
|
|
|
|
#ifdef _WIN32
|
|
//re-instate signal handler
|
|
IDRIS2_VERIFY(signal(signum, _collect_signal) != SIG_ERR, "signal failed: %s", strerror(errno));
|
|
#endif
|
|
}
|
|
|
|
#ifndef _WIN32
|
|
static inline struct sigaction _simple_handler(void (*handler)(int)) {
|
|
struct sigaction new_action;
|
|
|
|
new_action.sa_handler = handler;
|
|
sigemptyset (&new_action.sa_mask);
|
|
new_action.sa_flags = 0;
|
|
|
|
return new_action;
|
|
}
|
|
#endif
|
|
|
|
int ignore_signal(int signum) {
|
|
#ifdef _WIN32
|
|
return signal(signum, SIG_IGN) == SIG_ERR ? -1 : 0;
|
|
#else
|
|
struct sigaction handler = _simple_handler(SIG_IGN);
|
|
return sigaction(signum, &handler, NULL);
|
|
#endif
|
|
}
|
|
|
|
int default_signal(int signum) {
|
|
#ifdef _WIN32
|
|
return signal(signum, SIG_DFL) == SIG_ERR ? -1 : 0;
|
|
#else
|
|
struct sigaction handler = _simple_handler(SIG_DFL);
|
|
return sigaction(signum, &handler, NULL);
|
|
#endif
|
|
}
|
|
|
|
int collect_signal(int signum) {
|
|
#ifdef _WIN32
|
|
return signal(signum, _collect_signal) == SIG_ERR ? -1 : 0;
|
|
#else
|
|
struct sigaction handler = _simple_handler(_collect_signal);
|
|
return sigaction(signum, &handler, NULL);
|
|
#endif
|
|
}
|
|
|
|
int handle_next_collected_signal() {
|
|
uint32_t signals_snapshot = atomic_load(&signals);
|
|
if (signals_snapshot == 0) {
|
|
return -1;
|
|
}
|
|
for (uint32_t signum = 0; signum != N_SIGNALS; ++signum) {
|
|
uint32_t mask = (uint32_t) 1 << signum;
|
|
if ((signals_snapshot & mask) != 0) {
|
|
atomic_fetch_and(&signals, ~mask);
|
|
return signum;
|
|
}
|
|
}
|
|
abort();
|
|
}
|
|
|
|
int raise_signal(int signum) {
|
|
return raise(signum);
|
|
}
|
|
|
|
int send_signal(int pid, int signum) {
|
|
#ifdef _WIN32
|
|
// TODO: ignores pid
|
|
return raise_signal(signum);
|
|
#else
|
|
return kill(pid, signum);
|
|
#endif
|
|
}
|
|
|
|
int sighup() {
|
|
#ifdef _WIN32
|
|
return -1;
|
|
#else
|
|
return SIGHUP;
|
|
#endif
|
|
}
|
|
|
|
int sigint() {
|
|
return SIGINT;
|
|
}
|
|
|
|
int sigabrt() {
|
|
return SIGABRT;
|
|
}
|
|
|
|
int sigquit() {
|
|
#ifdef _WIN32
|
|
return -1;
|
|
#else
|
|
return SIGQUIT;
|
|
#endif
|
|
}
|
|
|
|
int sigill() {
|
|
return SIGILL;
|
|
}
|
|
|
|
int sigsegv() {
|
|
return SIGSEGV;
|
|
}
|
|
|
|
int sigtrap() {
|
|
#ifdef _WIN32
|
|
return -1;
|
|
#else
|
|
return SIGTRAP;
|
|
#endif
|
|
}
|
|
|
|
int sigfpe() {
|
|
return SIGFPE;
|
|
}
|
|
|
|
int sigusr1() {
|
|
#ifdef _WIN32
|
|
return -1;
|
|
#else
|
|
return SIGUSR1;
|
|
#endif
|
|
}
|
|
|
|
int sigusr2() {
|
|
#ifdef _WIN32
|
|
return -1;
|
|
#else
|
|
return SIGUSR2;
|
|
#endif
|
|
}
|
|
|