Skip to content

Commit

Permalink
Merge pull request #207 from tobiasdiez/win-support
Browse files Browse the repository at this point in the history
Fix compilation on Windows
  • Loading branch information
dimpase authored Nov 19, 2024
2 parents fa6d45d + 224b24c commit 6633f1e
Show file tree
Hide file tree
Showing 6 changed files with 106 additions and 58 deletions.
6 changes: 3 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: Run Sage CI for Linux/Cygwin/macOS
name: CI

## This GitHub Actions workflow provides:
##
Expand Down Expand Up @@ -51,7 +51,7 @@ concurrency:
cancel-in-progress: true

jobs:
cygwin-without-sage:
cygwin:
runs-on: windows-latest
strategy:
fail-fast: false
Expand Down Expand Up @@ -88,7 +88,7 @@ jobs:
strategy:
fail-fast: false
matrix:
os: ['macos-13', 'macos-latest', 'ubuntu-latest']
os: ['macos-13', 'macos-latest', 'ubuntu-latest', 'windows-latest']
python-version: ['3.10', '3.11', '3.12', '3.13-dev']
steps:
- name: Set up the repository
Expand Down
3 changes: 2 additions & 1 deletion meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ cxx = meson.get_compiler('cpp')
is_windows = host_machine.system() == 'windows'
is_cygwin = host_machine.system() == 'cygwin'
is_msvc = cc.get_id() == 'msvc'
is_mingw = cc.get_id()=='gcc' and host_machine.system()=='windows'

# Set preprocessor macros
# Disable .c line numbers in exception tracebacks
Expand Down Expand Up @@ -45,7 +46,7 @@ config.set('HAVE_TIME_H', cc.has_header('time.h') ? 1 : 0)
config.set('HAVE_SYS_WAIT_H', cc.has_header('sys/wait.h') ? 1 : 0)
config.set('HAVE_WINDOWS_H', cc.has_header('windows.h') ? 1 : 0)

config.set('HAVE_FORK', cc.has_function('fork') ? 1 : 0)
config.set('HAVE_FORK', (cc.has_function('fork') and not is_mingw) ? 1 : 0)
config.set('HAVE_KILL', cc.has_function('kill') ? 1 : 0)
config.set('HAVE_SIGPROCMASK', cc.has_function('sigprocmask') ? 1 : 0)
config.set('HAVE_SIGALTSTACK', cc.has_function('sigaltstack') ? 1 : 0)
Expand Down
9 changes: 9 additions & 0 deletions src/conftest.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,17 @@
import pathlib
import platform

from _pytest.nodes import Collector
from _pytest.doctest import DoctestModule

if platform.system() == "Windows":
collect_ignore = [
"cysignals/alarm.pyx",
"cysignals/pselect.pyx",
"cysignals/pysignals.pyx",
"cysignals/tests.pyx",
]


def pytest_collect_file(
file_path: pathlib.Path,
Expand Down
43 changes: 0 additions & 43 deletions src/cysignals/cysignals_config.h.in

This file was deleted.

79 changes: 69 additions & 10 deletions src/cysignals/implementation.c
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ Interrupt and signal handling for Cython
#include <stdlib.h>
#include <limits.h>
#include <errno.h>
#include <pthread.h>
#if HAVE_SYS_TYPES_H
#include <sys/types.h>
#endif
Expand Down Expand Up @@ -88,6 +87,9 @@ void custom_set_pending_signal(int sig){
#if HAVE_WINDOWS_H
#include <windows.h>
#endif
#if !_WIN32
#include <pthread.h>
#endif
#include "struct_signals.h"


Expand All @@ -108,9 +110,11 @@ static sigset_t default_sigmask;
static sigset_t sigmask_with_sigint;
#endif

#if !_WIN32
/* A trampoline to jump to after handling a signal. */
static cyjmp_buf trampoline_setup;
static sigjmp_buf trampoline;
#endif

static void setup_cysignals_handlers(void);
static void cysigs_interrupt_handler(int sig);
Expand Down Expand Up @@ -194,15 +198,23 @@ static inline void print_stderr_ptr(void *ptr)

/* Reset all signal handlers and the signal mask to their defaults. */
static inline void sig_reset_defaults(void) {
#ifdef SIGHUP
signal(SIGHUP, SIG_DFL);
#endif
signal(SIGINT, SIG_DFL);
#ifdef SIGQUIT
signal(SIGQUIT, SIG_DFL);
#endif
signal(SIGILL, SIG_DFL);
signal(SIGABRT, SIG_DFL);
signal(SIGFPE, SIG_DFL);
#ifdef SIGBUS
signal(SIGBUS, SIG_DFL);
#endif
signal(SIGSEGV, SIG_DFL);
#ifdef SIGALRM
signal(SIGALRM, SIG_DFL);
#endif
signal(SIGTERM, SIG_DFL);
#if HAVE_SIGPROCMASK
sigprocmask(SIG_SETMASK, &default_sigmask, NULL);
Expand All @@ -228,10 +240,14 @@ static inline void sigdie_for_sig(int sig, int inside)
sigdie(sig, "Unhandled SIGFPE during signal handling.");
else if (sig == SIGSEGV)
sigdie(sig, "Unhandled SIGSEGV during signal handling.");
#ifdef SIGBUS
else if (sig == SIGBUS)
sigdie(sig, "Unhandled SIGBUS during signal handling.");
#endif
#ifdef SIGQUIT
else if (sig == SIGQUIT)
sigdie(sig, NULL);
#endif
else
sigdie(sig, "Unknown signal during signal handling.");
}
Expand All @@ -244,10 +260,14 @@ static inline void sigdie_for_sig(int sig, int inside)
sigdie(sig, "Unhandled SIGFPE: An unhandled floating point exception occurred.");
else if (sig == SIGSEGV)
sigdie(sig, "Unhandled SIGSEGV: A segmentation fault occurred.");
#ifdef SIGBUS
else if (sig == SIGBUS)
sigdie(sig, "Unhandled SIGBUS: A bus error occurred.");
#endif
#ifdef SIGQUIT
else if (sig == SIGQUIT)
sigdie(sig, NULL);
#endif
else
sigdie(sig, "Unknown signal received.");
}
Expand Down Expand Up @@ -330,8 +350,22 @@ static void cygwin_setup_alt_stack() {

#endif /* CYGWIN && __x86_64__ */

void get_monotonic_time(struct timespec *ts) {
#ifdef _WIN32
LARGE_INTEGER frequency;
LARGE_INTEGER counter;

QueryPerformanceFrequency(&frequency);
QueryPerformanceCounter(&counter);

ts->tv_sec = counter.QuadPart / frequency.QuadPart;
ts->tv_nsec = (counter.QuadPart % frequency.QuadPart) * 1e9 / frequency.QuadPart;
#else
clock_gettime(CLOCK_MONOTONIC, ts);
#endif
}

/* Handler for SIGHUP, SIGINT, SIGALRM
/* Handler for SIGHUP, SIGINT, SIGALRM, SIGTERM
*
* Inside sig_on() (i.e. when cysigs.sig_on_count is positive), this
* raises an exception and jumps back to sig_on().
Expand All @@ -350,7 +384,7 @@ static void cysigs_interrupt_handler(int sig)
if (cysigs.debug_level >= 3) print_backtrace();
/* Store time of this signal, unless there is already a
* pending signal. */
if (!cysigs.interrupt_received) clock_gettime(CLOCK_MONOTONIC, &sigtime);
if (!cysigs.interrupt_received) get_monotonic_time(&sigtime);
}
#endif

Expand All @@ -361,8 +395,10 @@ static void cysigs_interrupt_handler(int sig)
/* Raise an exception so Python can see it */
do_raise_exception(sig);

#if !_WIN32
/* Jump back to sig_on() (the first one if there is a stack) */
siglongjmp(trampoline, sig);
#endif
}
}
else
Expand All @@ -376,7 +412,11 @@ static void cysigs_interrupt_handler(int sig)
/* If we are here, we cannot handle the interrupt immediately, so
* we store the signal number for later use. But make sure we
* don't overwrite a SIGHUP or SIGTERM which we already received. */
if (cysigs.interrupt_received != SIGHUP && cysigs.interrupt_received != SIGTERM)
if (
#ifdef SIGHUP
cysigs.interrupt_received != SIGHUP &&
#endif
cysigs.interrupt_received != SIGTERM)
{
cysigs.interrupt_received = sig;
custom_set_pending_signal(sig);
Expand All @@ -393,24 +433,28 @@ static void cysigs_signal_handler(int sig)
int inside = cysigs.inside_signal_handler;
cysigs.inside_signal_handler = 1;

if (inside == 0 && cysigs.sig_on_count > 0 && sig != SIGQUIT)
{
if (inside == 0 && cysigs.sig_on_count > 0
#ifdef SIGQUIT
&& sig != SIGQUIT
#endif
) {
/* We are inside sig_on(), so we can handle the signal! */
#if ENABLE_DEBUG_CYSIGNALS
if (cysigs.debug_level >= 1) {
print_stderr("\n*** SIG ");
print_stderr_long(sig);
print_stderr(" *** inside sig_on\n");
if (cysigs.debug_level >= 3) print_backtrace();
clock_gettime(CLOCK_MONOTONIC, &sigtime);
get_monotonic_time(&sigtime);
}
#endif

/* Raise an exception so Python can see it */
do_raise_exception(sig);

#if !_WIN32
/* Jump back to sig_on() (the first one if there is a stack) */
siglongjmp(trampoline, sig);
#endif
}
else
{
Expand All @@ -422,7 +466,7 @@ static void cysigs_signal_handler(int sig)
}
}


#if !_WIN32
/* A trampoline to jump to after handling a signal.
*
* The jump to sig_on() uses cylongjmp(), which does not restore the
Expand Down Expand Up @@ -506,6 +550,7 @@ static void setup_trampoline(void)
cylongjmp(trampoline_setup, 1);
}
}
#endif


/* This calls sig_raise_exception() to actually raise the exception. */
Expand All @@ -514,7 +559,7 @@ static void do_raise_exception(int sig)
#if ENABLE_DEBUG_CYSIGNALS
struct timespec raisetime;
if (cysigs.debug_level >= 2) {
clock_gettime(CLOCK_MONOTONIC, &raisetime);
get_monotonic_time(&raisetime);
long delta_ms = (raisetime.tv_sec - sigtime.tv_sec)*1000L + (raisetime.tv_nsec - sigtime.tv_nsec)/1000000L;
PyGILState_STATE gilstate = PyGILState_Ensure();
print_stderr("do_raise_exception(sig=");
Expand Down Expand Up @@ -608,6 +653,11 @@ static void setup_alt_stack(void)

static void setup_cysignals_handlers(void)
{
#ifdef _WIN32
signal(SIGINT, cysigs_interrupt_handler);
signal(SIGTERM, cysigs_interrupt_handler);
signal(SIGABRT, cysigs_signal_handler);
#else
struct sigaction sa;
memset(&sa, 0, sizeof(sa));

Expand Down Expand Up @@ -635,21 +685,30 @@ static void setup_cysignals_handlers(void)
/* Handlers for interrupt-like signals */
sa.sa_handler = cysigs_interrupt_handler;
sa.sa_flags = 0;
#ifdef SIGHUP
if (sigaction(SIGHUP, &sa, NULL)) {perror("cysignals sigaction"); exit(1);}
#endif
if (sigaction(SIGINT, &sa, NULL)) {perror("cysignals sigaction"); exit(1);}
#ifdef SIGALRM
if (sigaction(SIGALRM, &sa, NULL)) {perror("cysignals sigaction"); exit(1);}
#endif

/* Handlers for critical signals */
sa.sa_handler = cysigs_signal_handler;
/* Allow signals during signal handling, we have code to deal with
* this case. */
sa.sa_flags = SA_NODEFER | SA_ONSTACK;
#ifdef SIGQUIT
if (sigaction(SIGQUIT, &sa, NULL)) {perror("cysignals sigaction"); exit(1);}
#endif
if (sigaction(SIGILL, &sa, NULL)) {perror("cysignals sigaction"); exit(1);}
if (sigaction(SIGABRT, &sa, NULL)) {perror("cysignals sigaction"); exit(1);}
if (sigaction(SIGFPE, &sa, NULL)) {perror("cysignals sigaction"); exit(1);}
#ifdef SIGBUS
if (sigaction(SIGBUS, &sa, NULL)) {perror("cysignals sigaction"); exit(1);}
#endif
if (sigaction(SIGSEGV, &sa, NULL)) {perror("cysignals sigaction"); exit(1);}
#endif
}


Expand Down
Loading

0 comments on commit 6633f1e

Please sign in to comment.