Permalink
Browse files

Add a safepoint-based mechanism to avoid SIGALRM for the TIMER facility

  - Retrofits the signal-free timer thread for Windows (thanks to
    Anton Kovalenko) to POSIXy platforms.

  - Provide os_* functions in the C runtime which simulate the win32
    API for waitable timers.

Currently supported on Linux (timerfd), FreeBSD (kqueue), and SunOS
(completion ports).  A tentative (untested) implementation is
provided for Darwin's kqueue.
  • Loading branch information...
lichtblau committed Sep 13, 2012
1 parent 83fc8f3 commit 266ccb364ef5379abd1c0c7b0a2aa81c41753de6
@@ -327,15 +327,22 @@
;; for the purpose of stopping and starting the world around GC can be
;; performed using safepoints instead of signals. Enable this feature
;; to compile with safepoints and to use them for GC.
+ ;; (Replaces use of SIG_STOP_FOR_GC.)
; :sb-safepoint
;; When compiling with safepoints, the INTERRUPT-THREAD mechanism can
;; also use safepoints to roll the target thread to a point at which it
;; can be interrupted safely, instead of using a signal for this
;; purpose. Enable this feature in addition to :SB-SAFEPOINT to enable
;; such behaviour.
+ ;; (Replaces use of SIGPIPE, except to wake up syscalls.)
; :sb-thruption
+ ;; When compiling with safepoints and thruptions, the TIMER facility
+ ;; can replace its use of setitimer with a background thread.
+ ;; (Replaces use of SIGALRM.)
+ ; :sb-wtimer
+
;;
;; miscellaneous notes on other things which could have special significance
;; in the *FEATURES* list
View
@@ -194,6 +194,8 @@ sufficiently motivated to do lengthy fixes."
(defun deinit ()
(call-hooks "save" *save-hooks*)
+ #!+sb-wtimer
+ (itimer-emulation-deinit)
(when (rest (sb!thread:list-all-threads))
(error "Cannot save core with multiple threads running."))
(float-deinit)
@@ -191,6 +191,7 @@
(sb!thread:interrupt-thread (sb!thread::foreground-thread)
#'interrupt-it)))
+#!-sb-wtimer
(defun sigalrm-handler (signal info context)
(declare (ignore signal info context))
(declare (type system-area-pointer context))
@@ -227,6 +228,7 @@
(enable-interrupt sigbus #'sigbus-handler)
#!-linux
(enable-interrupt sigsys #'sigsys-handler)
+ #!-sb-wtimer
(enable-interrupt sigalrm #'sigalrm-handler)
#!-sb-thruption
(enable-interrupt sigpipe #'sigpipe-handler)
View
@@ -315,20 +315,87 @@ triggers."
(incf (%timer-expire-time timer) (%timer-repeat-interval timer))
(%schedule-timer timer))))))
+;;; setitimer is unavailable for win32, but we can emulate it when
+;;; threads are available -- using win32 waitable timers.
+;;;
+;;; Conversely, when we want to minimize signal use on POSIX, we emulate
+;;; win32 waitable timers using a timerfd-like portability layer in
+;;; the runtime.
+
+#!+sb-wtimer
+(define-alien-type wtimer
+ #!+win32 system-area-pointer ;HANDLE, but that's not defined yet
+ #!+sunos system-area-pointer ;struct os_wtimer *
+ #!+(or linux bsd) int)
+
+#!+sb-wtimer
+(progn
+ (define-alien-routine "os_create_wtimer" wtimer)
+ (define-alien-routine "os_wait_for_wtimer" int (wt wtimer))
+ (define-alien-routine "os_close_wtimer" void (wt wtimer))
+ (define-alien-routine "os_cancel_wtimer" void (wt wtimer))
+ (define-alien-routine "os_set_wtimer" void (wt wtimer) (sec int) (nsec int))
+
+ ;; scheduler lock already protects us
+
+ (defvar *waitable-timer-handle* nil)
+
+ (defvar *timer-thread* nil)
+
+ (defun get-waitable-timer ()
+ (assert (under-scheduler-lock-p))
+ (or *waitable-timer-handle*
+ (prog1
+ (setf *waitable-timer-handle* (os-create-wtimer))
+ (setf *timer-thread*
+ (sb!thread:make-thread
+ (lambda ()
+ (loop while
+ (or (zerop
+ (os-wait-for-wtimer *waitable-timer-handle*))
+ *waitable-timer-handle*)
+ doing (run-expired-timers)))
+ :ephemeral t
+ :name "System timer watchdog thread")))))
+
+ (defun itimer-emulation-deinit ()
+ (with-scheduler-lock ()
+ (when *timer-thread*
+ (sb!thread:terminate-thread *timer-thread*)
+ (sb!thread:join-thread *timer-thread* :default nil))
+ (when *waitable-timer-handle*
+ (os-close-wtimer *waitable-timer-handle*)
+ (setf *waitable-timer-handle* nil))))
+
+ (defun %clear-system-timer ()
+ (os-cancel-wtimer (get-waitable-timer)))
+
+ (defun %set-system-timer (sec nsec)
+ (os-set-wtimer (get-waitable-timer) sec nsec)))
+
;;; Expiring timers
-(defun real-time->sec-and-usec (time)
+(defun real-time->sec-and-nsec (time)
;; KLUDGE: Always leave 0.0001 second for other stuff in order to
;; avoid starvation.
- (let ((min-usec 100))
+ (let ((min-nsec 100000))
(if (minusp time)
- (list 0 min-usec)
+ (values 0 min-nsec)
(multiple-value-bind (s u) (floor time internal-time-units-per-second)
- (setf u (floor (* (/ u internal-time-units-per-second) 1000000)))
- (if (and (= 0 s) (< u min-usec))
+ (setf u (floor (* (/ u internal-time-units-per-second)
+ #.(expt 10 9))))
+ (if (and (= 0 s) (< u min-nsec))
;; 0 0 means "shut down the timer" for setitimer
- (list 0 min-usec)
- (list s u))))))
+ (values 0 min-nsec)
+ (values s u))))))
+
+#!-(or sb-wtimer win32)
+(progn
+ (defun %set-system-timer (sec nsec)
+ (sb!unix:unix-setitimer :real 0 0 sec (ceiling nsec 1000)))
+
+ (defun %clear-system-timer ()
+ (sb!unix:unix-setitimer :real 0 0 0 0)))
(defun set-system-timer ()
(assert (under-scheduler-lock-p))
@@ -337,9 +404,9 @@ triggers."
(if next-timer
(let ((delta (- (%timer-expire-time next-timer)
(get-internal-real-time))))
- (apply #'sb!unix:unix-setitimer
- :real 0 0 (real-time->sec-and-usec delta)))
- (sb!unix:unix-setitimer :real 0 0 0 0))))
+ (multiple-value-call #'%set-system-timer
+ (real-time->sec-and-nsec delta)))
+ (%clear-system-timer))))
(defun run-timer (timer)
(let ((function (%timer-interrupt-function timer))
View
@@ -45,6 +45,11 @@
#if defined LISP_FEATURE_GENCGC
#include "gencgc-internal.h"
#endif
+
+#if defined(LISP_FEATURE_SB_WTIMER) && !defined(LISP_FEATURE_DARWIN)
+# include <sys/event.h>
+#endif
+
os_vm_size_t os_vm_page_size;
@@ -567,3 +572,60 @@ os_dlsym(void *handle, const char *symbol)
}
#endif
+
+#if defined(LISP_FEATURE_SB_WTIMER) && !defined(LISP_FEATURE_DARWIN)
+/*
+ * Waitable timer implementation for the safepoint-based (SIGALRM-free)
+ * timer facility using kqueue.
+ */
+int
+os_create_wtimer()
+{
+ int kq = kqueue();
+ if (kq == -1)
+ lose("os_create_wtimer: kqueue");
+ return kq;
+}
+
+int
+os_wait_for_wtimer(int kq)
+{
+ struct kevent ev;
+ int n;
+ if ( (n = kevent(kq, 0, 0, &ev, 1, 0)) == -1) {
+ if (errno != EINTR)
+ lose("os_wtimer_listen failed");
+ n = 0;
+ }
+ return n != 1;
+}
+
+void
+os_close_wtimer(int kq)
+{
+ if (close(kq) == -1)
+ lose("os_close_wtimer failed");
+}
+
+void
+os_set_wtimer(int kq, int sec, int nsec)
+{
+ long long msec
+ = ((long long) sec) * 1000 + (long long) (nsec+999999) / 1000000;
+ if (msec > INT_MAX) msec = INT_MAX;
+
+ struct kevent ev;
+ EV_SET(&ev, 1, EVFILT_TIMER, EV_ADD|EV_ENABLE|EV_ONESHOT, 0, (int)msec, 0);
+ if (kevent(kq, &ev, 1, 0, 0, 0) == -1)
+ perror("os_set_wtimer: kevent");
+}
+
+void
+os_cancel_wtimer(int kq)
+{
+ struct kevent ev;
+ EV_SET(&ev, 1, EVFILT_TIMER, EV_DISABLE, 0, 0, 0);
+ if (kevent(kq, &ev, 1, 0, 0, 0) == -1 && errno != ENOENT)
+ perror("os_cancel_wtimer: kevent");
+}
+#endif
View
@@ -31,6 +31,12 @@
#include <stdlib.h>
#endif
+#if defined(LISP_FEATURE_SB_WTIMER)
+# include <sys/types.h>
+# include <sys/event.h>
+# include <sys/time.h>
+#endif
+
char *
os_get_runtime_executable_path(int external)
{
@@ -301,3 +307,71 @@ os_sem_destroy(os_sem_t *sem)
}
#endif
+
+#if defined(LISP_FEATURE_SB_WTIMER)
+
+# error Completely untested. Go ahead! Remove this line, try your luck!
+
+/*
+ * Waitable timer implementation for the safepoint-based (SIGALRM-free)
+ * timer facility using kqueue.
+ *
+ * Unlike FreeBSD with its ms (!) timer resolution, Darwin supports ns
+ * timer resolution -- or at least it pretends to do so on the API
+ * level (?). To use it, we need the *64 versions of the functions and
+ * structures.
+ *
+ * Unfortunately, I don't run Darwin, and can't test this code, so it's
+ * just a hopeful translation from FreeBSD.
+ */
+
+int
+os_create_wtimer()
+{
+ int kq = kqueue();
+ if (kq == -1)
+ lose("os_create_wtimer: kqueue");
+ return kq;
+}
+
+int
+os_wait_for_wtimer(int kq)
+{
+ struct kevent64_s ev;
+ int n;
+ if ( (n = kevent64(kq, 0, 0, &ev, 1, 0, 0)) == -1) {
+ if (errno != EINTR)
+ lose("os_wtimer_listen failed");
+ n = 0;
+ }
+ return n != 1;
+}
+
+void
+os_close_wtimer(int kq)
+{
+ if (close(kq) == -1)
+ lose("os_close_wtimer failed");
+}
+
+void
+os_set_wtimer(int kq, int sec, int nsec)
+{
+ int64_t nsec = ((int64_t) sec) * 1000000000 + (int64_t) nsec;
+
+ struct kevent64_s ev;
+ EV_SET64(&ev, 1, EVFILT_TIMER, EV_ADD|EV_ENABLE|EV_ONESHOT, NOTE_NSECONDS,
+ nsec, 0, 0, 0);
+ if (kevent64(kq, &ev, 1, 0, 0, 0, 0) == -1)
+ perror("os_set_wtimer: kevent");
+}
+
+void
+os_cancel_wtimer(int kq)
+{
+ struct kevent64_s ev;
+ EV_SET64(&ev, 1, EVFILT_TIMER, EV_DISABLE, 0, 0, 0, 0, 0);
+ if (kevent64(kq, &ev, 1, 0, 0, 0, 0) == -1 && errno != ENOENT)
+ perror("os_cancel_wtimer: kevent");
+}
+#endif
View
@@ -55,6 +55,10 @@
#else
#include "cheneygc-internal.h"
#endif
+#include <fcntl.h>
+#ifdef LISP_FEATURE_SB_WTIMER
+# include <sys/timerfd.h>
+#endif
#ifdef LISP_FEATURE_X86
/* Prototype for personality(2). Done inline here since the header file
@@ -482,3 +486,61 @@ os_get_runtime_executable_path(int external)
return copied_string(path);
}
+
+#ifdef LISP_FEATURE_SB_WTIMER
+/*
+ * Waitable timer implementation for the safepoint-based (SIGALRM-free)
+ * timer facility using timerfd_create().
+ */
+int
+os_create_wtimer()
+{
+ int fd = timerfd_create(CLOCK_MONOTONIC, 0);
+ if (fd == -1)
+ lose("os_create_wtimer: timerfd_create");
+
+ /* Cannot count on TFD_CLOEXEC availability, so do it manually: */
+ if (fcntl(fd, F_SETFD, FD_CLOEXEC) == -1)
+ lose("os_create_wtimer: fcntl");
+
+ return fd;
+}
+
+int
+os_wait_for_wtimer(int fd)
+{
+ unsigned char buf[8];
+ int n = read(fd, buf, sizeof(buf));
+ if (n == -1) {
+ if (errno == EINTR)
+ return -1;
+ lose("os_wtimer_listen failed");
+ }
+ if (n != sizeof(buf))
+ lose("os_wtimer_listen read too little");
+ return 0;
+}
+
+void
+os_close_wtimer(int fd)
+{
+ if (close(fd) == -1)
+ lose("os_close_wtimer failed");
+}
+
+void
+os_set_wtimer(int fd, int sec, int nsec)
+{
+ struct itimerspec spec = { {0,0}, {0,0} };
+ spec.it_value.tv_sec = sec;
+ spec.it_value.tv_nsec = nsec;
+ if (timerfd_settime(fd, 0, &spec, 0) == -1)
+ lose("timerfd_settime");
+}
+
+void
+os_cancel_wtimer(int fd)
+{
+ os_set_wtimer(fd, 0, 0);
+}
+#endif
Oops, something went wrong.

0 comments on commit 266ccb3

Please sign in to comment.