;; 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
;; 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
(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)
(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))
(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)
(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))
(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))
#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
+
\f
os_vm_size_t os_vm_page_size;
}
#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
#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)
{
}
#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
#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
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
#include "gencgc-internal.h"
#endif
+#ifdef LISP_FEATURE_SB_WTIMER
+# include <port.h>
+# include <time.h>
+# include <errno.h>
+#endif
+
os_vm_size_t os_vm_page_size=0;
void
return copied_string(path);
}
+#ifdef LISP_FEATURE_SB_WTIMER
+/*
+ * Waitable timer implementation for the safepoint-based (SIGALRM-free)
+ * timer facility using SunOS completion ports.
+ */
+
+struct os_wtimer {
+ int port;
+ int timer;
+};
+
+struct os_wtimer *
+os_create_wtimer()
+{
+ int port = port_create();
+ if (port == -1) {
+ perror("port_create");
+ lose("os_create_wtimer");
+ }
+
+ port_notify_t pn;
+ pn.portnfy_port = port;
+ pn.portnfy_user = 0;
+
+ struct sigevent ev;
+ memset(&ev, 0, sizeof(ev));
+ ev.sigev_notify = SIGEV_PORT;
+ ev.sigev_value.sival_ptr = &pn;
+
+ timer_t timer;
+ if (timer_create(CLOCK_HIGHRES, &ev, &timer) == -1
+ && (errno != EPERM || timer_create(CLOCK_REALTIME, &ev, &timer) == -1))
+ {
+ perror("timer_create");
+ lose("os_create_wtimer");
+ }
+
+ struct os_wtimer *wt = malloc(sizeof(struct os_wtimer));
+ if (!wt)
+ lose("os_create_wtimer: malloc");
+
+ wt->port = port;
+ wt->timer = timer;
+ return wt;
+}
+
+int
+os_wait_for_wtimer(struct os_wtimer *wt)
+{
+ port_event_t pe;
+ if (port_get(wt->port, &pe, 0) == -1) {
+ if (errno == EINTR)
+ return 1;
+ perror("port_get");
+ lose("os_wtimer_listen failed");
+ }
+ return 0;
+}
+
+void
+os_close_wtimer(struct os_wtimer *wt)
+{
+ if (close(wt->port) == -1) {
+ perror("close");
+ lose("os_close_wtimer");
+ }
+ if (timer_delete(wt->timer) == -1) {
+ perror("timer_delete");
+ lose("os_close_wtimer");
+ }
+ free(wt);
+}
+
+void
+os_set_wtimer(struct os_wtimer *wt, int sec, int nsec)
+{
+ struct itimerspec spec;
+ spec.it_value.tv_sec = sec;
+ spec.it_value.tv_nsec = nsec;
+ spec.it_interval.tv_sec = 0;
+ spec.it_interval.tv_nsec = 0;
+ if (timer_settime(wt->timer, 0, &spec, 0) == -1) {
+ int x = errno;
+ perror("timer_settime");
+ if (x != EINVAL)
+ lose("os_set_wtimer");
+ }
+}
+
+void
+os_cancel_wtimer(struct os_wtimer *wt)
+{
+ os_set_wtimer(wt, 0, 0);
+}
+#endif