+(defvar *threads-to-kill*)
+(defvar *threads-to-join*)
+
+(eval-when (:compile-toplevel :load-toplevel :execute)
+ (require :sb-posix))
+
+(sb-posix:putenv (format nil "SBCL_MACHINE_TYPE=~A" (machine-type)))
+(sb-posix:putenv (format nil "SBCL_SOFTWARE_TYPE=~A" (software-type)))
+
+#+sb-thread
+(defun make-kill-thread (&rest args)
+ (let ((thread (apply #'sb-thread:make-thread args)))
+ (when (boundp '*threads-to-kill*)
+ (push thread *threads-to-kill*))
+ thread))
+
+#+sb-thread
+(defun make-join-thread (&rest args)
+ (let ((thread (apply #'sb-thread:make-thread args)))
+ (when (boundp '*threads-to-join*)
+ (push thread *threads-to-join*))
+ thread))
+
+(defun log-msg (&rest args)
+ (format *trace-output* "~&::: ")
+ (apply #'format *trace-output* args)
+ (terpri *trace-output*)
+ (force-output *trace-output*))
+
+(defmacro with-test ((&key fails-on broken-on skipped-on name)
+ &body body)
+ (let ((block-name (gensym))
+ #+sb-thread (threads (gensym "THREADS")))
+ (flet ((name-ok (x y)
+ (declare (ignore y))
+ (typecase x
+ (symbol (let ((package (symbol-package x)))
+ (or (null package)
+ (eql package (find-package "CL"))
+ (eql package (find-package "KEYWORD"))
+ (eql (mismatch "SB-" (package-name package)) 3))))
+ (integer t))))
+ (unless (tree-equal name name :test #'name-ok)
+ (error "test name must be all-keywords: ~S" name)))
+ `(progn
+ (start-test)
+ (cond
+ ((broken-p ,broken-on)
+ (fail-test :skipped-broken ',name "Test broken on this platform"))
+ ((skipped-p ,skipped-on)
+ (fail-test :skipped-disabled ',name "Test disabled for this combination of platform and features"))
+ (t
+ (let (#+sb-thread (,threads (sb-thread:list-all-threads))
+ (*threads-to-join* nil)
+ (*threads-to-kill* nil))
+ (block ,block-name
+ (handler-bind ((error (lambda (error)
+ (if (expected-failure-p ,fails-on)
+ (fail-test :expected-failure ',name error)
+ (fail-test :unexpected-failure ',name error))
+ (return-from ,block-name))))
+ (progn
+ (log-msg "Running ~S" ',name)
+ ,@body
+ #+sb-thread
+ (let ((any-leftover nil))
+ (dolist (thread *threads-to-join*)
+ (ignore-errors (sb-thread:join-thread thread)))
+ (dolist (thread *threads-to-kill*)
+ (ignore-errors (sb-thread:terminate-thread thread)))
+ (setf ,threads (union (union *threads-to-kill*
+ *threads-to-join*)
+ ,threads))
+ #+(and sb-safepoint-strictly (not win32))
+ (dolist (thread (sb-thread:list-all-threads))
+ (when (typep thread 'sb-thread:signal-handling-thread)
+ (ignore-errors (sb-thread:join-thread thread))))
+ (dolist (thread (sb-thread:list-all-threads))
+ (unless (or (not (sb-thread:thread-alive-p thread))
+ (eql thread sb-thread:*current-thread*)
+ (member thread ,threads)
+ (sb-thread:thread-ephemeral-p thread))
+ (setf any-leftover thread)
+ (ignore-errors (sb-thread:terminate-thread thread))))
+ (when any-leftover
+ (fail-test :leftover-thread ',name any-leftover)
+ (return-from ,block-name)))
+ (if (expected-failure-p ,fails-on)
+ (fail-test :unexpected-success ',name nil)
+ (log-msg "Success ~S" ',name)))))))))))