(in-package :stumpwm-user) (defvar *main-thread*) (defvar *watchdog-thread* nil) (defvar *watching* nil) (defvar *last-heartbeat* 0) (defparameter *watch-timeout* 20) (defun watch-heartbeat () (setf *last-heartbeat* (get-universal-time))) (defun watch-this-thread () (setf *main-thread* sb-thread:*current-thread*) (watch-heartbeat)) (watch-this-thread) (add-hook *internal-loop-hook* 'watch-heartbeat) (defun int-watched (function) (sb-thread:interrupt-thread *main-thread* function)) (defun int-error (&optional (msg "Interrupted")) (int-watched (lambda () (error msg)))) (defun get-backtrace () (let ((ret nil) (sem (sb-thread:make-semaphore :name "backtrace-semaphore"))) (int-watched (lambda () (let ((backtrace (sb-debug:backtrace-as-list))) (setf ret backtrace) (sb-thread:signal-semaphore sem)))) (sb-thread:wait-on-semaphore sem) ret)) (defun save-backtrace (backtrace) nil) (defun watch-function () (setf *watching* t) (loop (let ((now (get-universal-time))) (if (> (- now *last-heartbeat*) *watch-timeout*) (progn (save-backtrace (get-backtrace)) (int-watched (lambda () (error "Interrupted from watchdog")))))) (sleep 10) (if (not *watching*) (return-from watch-function (values))))) (defun ensure-watching () (if (or (null *watchdog-thread*) (not (sb-thread:thread-alive-p *watchdog-thread*))) (progn (setf *watchdog-thread* (sb-thread:make-thread 'watch-function :name "watchdog-thread")) t) nil)) (ensure-watching)