848 lines
29 KiB
Common Lisp
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

;;;; -*- indent-tabs-mode: nil; outline-regexp: ";;;;;*"; -*-
;;;
;;; swank-abcl.lisp --- Armedbear CL specific code for SLIME.
;;;
;;; Adapted from swank-acl.lisp, Andras Simon, 2004
;;;
;;; This code has been placed in the Public Domain. All warranties
;;; are disclaimed.
;;;
(defpackage swank/abcl
(:use cl swank/backend))
(in-package swank/abcl)
(eval-when (:compile-toplevel :load-toplevel :execute)
(require :collect) ;just so that it doesn't spoil the flying letters
(require :pprint)
(require :gray-streams)
(assert (>= (read-from-string (subseq (lisp-implementation-version) 0 4))
0.22)
() "This file needs ABCL version 0.22 or newer"))
(defimplementation gray-package-name ()
"GRAY-STREAMS")
;; FIXME: switch to shared Gray stream implementation when bugs are
;; fixed in ABCL. See: http://abcl.org/trac/ticket/373.
(progn
(defimplementation make-output-stream (write-string)
(ext:make-slime-output-stream write-string))
(defimplementation make-input-stream (read-string)
(ext:make-slime-input-stream read-string
(make-synonym-stream '*standard-output*))))
(defimplementation call-with-compilation-hooks (function)
(funcall function))
;;; swank-mop
;;dummies and definition
(defclass standard-slot-definition ()())
;(defun class-finalized-p (class) t)
(defun slot-definition-documentation (slot)
(declare (ignore slot))
#+nil (documentation slot 't))
(defun slot-definition-type (slot)
(declare (ignore slot))
t)
(defun class-prototype (class)
(declare (ignore class))
nil)
(defun generic-function-declarations (gf)
(declare (ignore gf))
nil)
(defun specializer-direct-methods (spec)
(mop:class-direct-methods spec))
(defun slot-definition-name (slot)
(mop:slot-definition-name slot))
(defun class-slots (class)
(mop:class-slots class))
(defun method-generic-function (method)
(mop:method-generic-function method))
(defun method-function (method)
(mop:method-function method))
(defun slot-boundp-using-class (class object slotdef)
(declare (ignore class))
(system::slot-boundp object (slot-definition-name slotdef)))
(defun slot-value-using-class (class object slotdef)
(declare (ignore class))
(system::slot-value object (slot-definition-name slotdef)))
(import-to-swank-mop
'( ;; classes
cl:standard-generic-function
standard-slot-definition ;;dummy
cl:method
cl:standard-class
#+#.(swank/backend:with-symbol 'compute-applicable-methods-using-classes
'mop)
mop:compute-applicable-methods-using-classes
;; standard-class readers
mop:class-default-initargs
mop:class-direct-default-initargs
mop:class-direct-slots
mop:class-direct-subclasses
mop:class-direct-superclasses
mop:eql-specializer
mop:class-finalized-p
mop:finalize-inheritance
cl:class-name
mop:class-precedence-list
class-prototype ;;dummy
class-slots
specializer-direct-methods
;; eql-specializer accessors
mop::eql-specializer-object
;; generic function readers
mop:generic-function-argument-precedence-order
generic-function-declarations ;;dummy
mop:generic-function-lambda-list
mop:generic-function-methods
mop:generic-function-method-class
mop:generic-function-method-combination
mop:generic-function-name
;; method readers
method-generic-function
method-function
mop:method-lambda-list
mop:method-specializers
mop:method-qualifiers
;; slot readers
mop:slot-definition-allocation
slot-definition-documentation ;;dummy
mop:slot-definition-initargs
mop:slot-definition-initform
mop:slot-definition-initfunction
slot-definition-name
slot-definition-type ;;dummy
mop:slot-definition-readers
mop:slot-definition-writers
slot-boundp-using-class
slot-value-using-class
mop:slot-makunbound-using-class))
;;;; TCP Server
(defimplementation preferred-communication-style ()
:spawn)
(defimplementation create-socket (host port &key backlog)
(ext:make-server-socket port))
(defimplementation local-port (socket)
(java:jcall (java:jmethod "java.net.ServerSocket" "getLocalPort") socket))
(defimplementation close-socket (socket)
(ext:server-socket-close socket))
(defimplementation accept-connection (socket
&key external-format buffering timeout)
(declare (ignore buffering timeout))
(ext:get-socket-stream (ext:socket-accept socket)
:element-type (if external-format
'character
'(unsigned-byte 8))
:external-format (or external-format :default)))
;;;; UTF8
;; faster please!
(defimplementation string-to-utf8 (s)
(jbytes-to-octets
(java:jcall
(java:jmethod "java.lang.String" "getBytes" "java.lang.String")
s
"UTF8")))
(defimplementation utf8-to-string (u)
(java:jnew
(java:jconstructor "org.armedbear.lisp.SimpleString"
"java.lang.String")
(java:jnew (java:jconstructor "java.lang.String" "[B" "java.lang.String")
(octets-to-jbytes u)
"UTF8")))
(defun octets-to-jbytes (octets)
(declare (type octets (simple-array (unsigned-byte 8) (*))))
(let* ((len (length octets))
(bytes (java:jnew-array "byte" len)))
(loop for byte across octets
for i from 0
do (java:jstatic (java:jmethod "java.lang.reflect.Array" "setByte"
"java.lang.Object" "int" "byte")
"java.lang.relect.Array"
bytes i byte))
bytes))
(defun jbytes-to-octets (jbytes)
(let* ((len (java:jarray-length jbytes))
(octets (make-array len :element-type '(unsigned-byte 8))))
(loop for i from 0 below len
for jbyte = (java:jarray-ref jbytes i)
do (setf (aref octets i) jbyte))
octets))
;;;; External formats
(defvar *external-format-to-coding-system*
'((:iso-8859-1 "latin-1" "iso-latin-1" "iso-8859-1")
((:iso-8859-1 :eol-style :lf)
"latin-1-unix" "iso-latin-1-unix" "iso-8859-1-unix")
(:utf-8 "utf-8")
((:utf-8 :eol-style :lf) "utf-8-unix")
(:euc-jp "euc-jp")
((:euc-jp :eol-style :lf) "euc-jp-unix")
(:us-ascii "us-ascii")
((:us-ascii :eol-style :lf) "us-ascii-unix")))
(defimplementation find-external-format (coding-system)
(car (rassoc-if (lambda (x)
(member coding-system x :test #'equal))
*external-format-to-coding-system*)))
;;;; Unix signals
(defimplementation getpid ()
(handler-case
(let* ((runtime
(java:jstatic "getRuntime" "java.lang.Runtime"))
(command
(java:jnew-array-from-array
"java.lang.String" #("sh" "-c" "echo $PPID")))
(runtime-exec-jmethod
;; Complicated because java.lang.Runtime.exec() is
;; overloaded on a non-primitive type (array of
;; java.lang.String), so we have to use the actual
;; parameter instance to get java.lang.Class
(java:jmethod "java.lang.Runtime" "exec"
(java:jcall
(java:jmethod "java.lang.Object" "getClass")
command)))
(process
(java:jcall runtime-exec-jmethod runtime command))
(output
(java:jcall (java:jmethod "java.lang.Process" "getInputStream")
process)))
(java:jcall (java:jmethod "java.lang.Process" "waitFor")
process)
(loop :with b :do
(setq b
(java:jcall (java:jmethod "java.io.InputStream" "read")
output))
:until (member b '(-1 #x0a)) ; Either EOF or LF
:collecting (code-char b) :into result
:finally (return
(parse-integer (coerce result 'string)))))
(t () 0)))
(defimplementation lisp-implementation-type-name ()
"armedbear")
(defimplementation set-default-directory (directory)
(let ((dir (sys::probe-directory directory)))
(when dir (setf *default-pathname-defaults* dir))
(namestring dir)))
;;;; Misc
(defimplementation arglist (fun)
(cond ((symbolp fun)
(multiple-value-bind (arglist present)
(sys::arglist fun)
(when (and (not present)
(fboundp fun)
(typep (symbol-function fun)
'standard-generic-function))
(setq arglist
(mop::generic-function-lambda-list (symbol-function fun))
present
t))
(if present arglist :not-available)))
(t :not-available)))
(defimplementation function-name (function)
(nth-value 2 (function-lambda-expression function)))
(defimplementation macroexpand-all (form &optional env)
(ext:macroexpand-all form env))
(defimplementation collect-macro-forms (form &optional env)
;; Currently detects only normal macros, not compiler macros.
(declare (ignore env))
(with-collected-macro-forms (macro-forms)
(handler-bind ((warning #'muffle-warning))
(ignore-errors
(compile nil `(lambda () ,(macroexpand-all form env)))))
(values macro-forms nil)))
(defimplementation describe-symbol-for-emacs (symbol)
(let ((result '()))
(flet ((doc (kind &optional (sym symbol))
(or (documentation sym kind) :not-documented))
(maybe-push (property value)
(when value
(setf result (list* property value result)))))
(maybe-push
:variable (when (boundp symbol)
(doc 'variable)))
(when (fboundp symbol)
(maybe-push
(cond ((macro-function symbol) :macro)
((special-operator-p symbol) :special-operator)
((typep (fdefinition symbol) 'generic-function)
:generic-function)
(t :function))
(doc 'function)))
(maybe-push
:class (if (find-class symbol nil)
(doc 'class)))
result)))
(defimplementation describe-definition (symbol namespace)
(ecase namespace
((:variable :macro)
(describe symbol))
((:function :generic-function)
(describe (symbol-function symbol)))
(:class
(describe (find-class symbol)))))
(defimplementation describe-definition (symbol namespace)
(ecase namespace
(:variable
(describe symbol))
((:function :generic-function)
(describe (symbol-function symbol)))
(:class
(describe (find-class symbol)))))
;;;; Debugger
;; Copied from swank-sbcl.lisp.
;;
;; Notice that *INVOKE-DEBUGGER-HOOK* is tried before *DEBUGGER-HOOK*,
;; so we have to make sure that the latter gets run when it was
;; established locally by a user (i.e. changed meanwhile.)
(defun make-invoke-debugger-hook (hook)
(lambda (condition old-hook)
(if *debugger-hook*
(funcall *debugger-hook* condition old-hook)
(funcall hook condition old-hook))))
(defimplementation call-with-debugger-hook (hook fun)
(let ((*debugger-hook* hook)
(sys::*invoke-debugger-hook* (make-invoke-debugger-hook hook)))
(funcall fun)))
(defimplementation install-debugger-globally (function)
(setq *debugger-hook* function)
(setq sys::*invoke-debugger-hook* (make-invoke-debugger-hook function)))
(defvar *sldb-topframe*)
(defimplementation call-with-debugging-environment (debugger-loop-fn)
(let* ((magic-token (intern "SWANK-DEBUGGER-HOOK" 'swank))
(*sldb-topframe*
(second (member magic-token (sys:backtrace)
:key (lambda (frame)
(first (sys:frame-to-list frame)))))))
(funcall debugger-loop-fn)))
(defun backtrace (start end)
"A backtrace without initial SWANK frames."
(let ((backtrace (sys:backtrace)))
(subseq (or (member *sldb-topframe* backtrace) backtrace)
start end)))
(defun nth-frame (index)
(nth index (backtrace 0 nil)))
(defimplementation compute-backtrace (start end)
(let ((end (or end most-positive-fixnum)))
(backtrace start end)))
(defimplementation print-frame (frame stream)
(write-string (sys:frame-to-string frame)
stream))
;;; Sorry, but can't seem to declare DEFIMPLEMENTATION under FLET.
;;; --ME 20150403
(defun nth-frame-list (index)
(java:jcall "toLispList" (nth-frame index)))
(defun match-lambda (operator values)
(jvm::match-lambda-list
(multiple-value-list
(jvm::parse-lambda-list (ext:arglist operator)))
values))
(defimplementation frame-locals (index)
(loop
:for id :upfrom 0
:with frame = (nth-frame-list index)
:with operator = (first frame)
:with values = (rest frame)
:with arglist = (if (and operator (consp values) (not (null values)))
(handler-case
(match-lambda operator values)
(jvm::lambda-list-mismatch (e)
:lambda-list-mismatch))
:not-available)
:for value :in values
:collecting (list
:name (if (not (keywordp arglist))
(first (nth id arglist))
(format nil "arg~A" id))
:id id
:value value)))
(defimplementation frame-var-value (index id)
(elt (rest (java:jcall "toLispList" (nth-frame index))) id))
#+nil
(defimplementation disassemble-frame (index)
(disassemble (debugger:frame-function (nth-frame index))))
(defimplementation frame-source-location (index)
(let ((frame (nth-frame index)))
(or (source-location (nth-frame index))
`(:error ,(format nil "No source for frame: ~a" frame)))))
#+nil
(defimplementation eval-in-frame (form frame-number)
(debugger:eval-form-in-context
form
(debugger:environment-of-frame (nth-frame frame-number))))
#+nil
(defimplementation return-from-frame (frame-number form)
(let ((frame (nth-frame frame-number)))
(multiple-value-call #'debugger:frame-return
frame (debugger:eval-form-in-context
form
(debugger:environment-of-frame frame)))))
;;; XXX doesn't work for frames with arguments
#+nil
(defimplementation restart-frame (frame-number)
(let ((frame (nth-frame frame-number)))
(debugger:frame-retry frame (debugger:frame-function frame))))
;;;; Compiler hooks
(defvar *buffer-name* nil)
(defvar *buffer-start-position*)
(defvar *buffer-string*)
(defvar *compile-filename*)
(defvar *abcl-signaled-conditions*)
(defun handle-compiler-warning (condition)
(let ((loc (when (and jvm::*compile-file-pathname*
system::*source-position*)
(cons jvm::*compile-file-pathname* system::*source-position*))))
;; filter condition signaled more than once.
(unless (member condition *abcl-signaled-conditions*)
(push condition *abcl-signaled-conditions*)
(signal 'compiler-condition
:original-condition condition
:severity :warning
:message (format nil "~A" condition)
:location (cond (*buffer-name*
(make-location
(list :buffer *buffer-name*)
(list :offset *buffer-start-position* 0)))
(loc
(destructuring-bind (file . pos) loc
(make-location
(list :file (namestring (truename file)))
(list :position (1+ pos)))))
(t
(make-location
(list :file (namestring *compile-filename*))
(list :position 1))))))))
(defimplementation swank-compile-file (input-file output-file
load-p external-format
&key policy)
(declare (ignore external-format policy))
(let ((jvm::*resignal-compiler-warnings* t)
(*abcl-signaled-conditions* nil))
(handler-bind ((warning #'handle-compiler-warning))
(let ((*buffer-name* nil)
(*compile-filename* input-file))
(multiple-value-bind (fn warn fail)
(compile-file input-file :output-file output-file)
(values fn warn
(and fn load-p
(not (load fn)))))))))
(defimplementation swank-compile-string (string &key buffer position filename
policy)
(declare (ignore filename policy))
(let ((jvm::*resignal-compiler-warnings* t)
(*abcl-signaled-conditions* nil))
(handler-bind ((warning #'handle-compiler-warning))
(let ((*buffer-name* buffer)
(*buffer-start-position* position)
(*buffer-string* string)
(sys::*source* (make-pathname :device "emacs-buffer" :name buffer))
(sys::*source-position* position))
(funcall (compile nil (read-from-string
(format nil "(~S () ~A)" 'lambda string))))
t))))
#|
;;;; Definition Finding
(defun find-fspec-location (fspec type)
(let ((file (excl::fspec-pathname fspec type)))
(etypecase file
(pathname
(let ((start (scm:find-definition-in-file fspec type file)))
(make-location (list :file (namestring (truename file)))
(if start
(list :position (1+ start))
(list :function-name (string fspec))))))
((member :top-level)
(list :error (format nil "Defined at toplevel: ~A" fspec)))
(null
(list :error (format nil "Unkown source location for ~A" fspec))))))
(defun fspec-definition-locations (fspec)
(let ((defs (excl::find-multiple-definitions fspec)))
(loop for (fspec type) in defs
collect (list fspec (find-fspec-location fspec type)))))
(defimplementation find-definitions (symbol)
(fspec-definition-locations symbol))
|#
(defgeneric source-location (object))
(defmethod source-location ((symbol symbol))
(when (pathnamep (ext:source-pathname symbol))
(let ((pos (ext:source-file-position symbol))
(path (namestring (ext:source-pathname symbol))))
(cond ((ext:pathname-jar-p path)
`(:location
;; strip off "jar:file:" = 9 characters
(:zip ,@(split-string (subseq path 9) "!/"))
;; pos never seems right. Use function name.
(:function-name ,(string symbol))
(:align t)))
((equal (pathname-device (ext:source-pathname symbol)) "emacs-buffer")
;; conspire with swank-compile-string to keep the buffer
;; name in a pathname whose device is "emacs-buffer".
`(:location
(:buffer ,(pathname-name (ext:source-pathname symbol)))
(:function-name ,(string symbol))
(:align t)))
(t
`(:location
(:file ,path)
,(if pos
(list :position (1+ pos))
(list :function-name (string symbol)))
(:align t)))))))
(defmethod source-location ((frame sys::java-stack-frame))
(destructuring-bind (&key class method file line) (sys:frame-to-list frame)
(declare (ignore method))
(let ((file (or (find-file-in-path file *source-path*)
(let ((f (format nil "~{~a/~}~a"
(butlast (split-string class "\\."))
file)))
(find-file-in-path f *source-path*)))))
(and file
`(:location ,file (:line ,line) ())))))
(defmethod source-location ((frame sys::lisp-stack-frame))
(destructuring-bind (operator &rest args) (sys:frame-to-list frame)
(declare (ignore args))
(etypecase operator
(function (source-location operator))
(list nil)
(symbol (source-location operator)))))
(defmethod source-location ((fun function))
(let ((name (function-name fun)))
(and name (source-location name))))
(defun system-property (name)
(java:jstatic "getProperty" "java.lang.System" name))
(defun pathname-parent (pathname)
(make-pathname :directory (butlast (pathname-directory pathname))))
(defun pathname-absolute-p (pathname)
(eq (car (pathname-directory pathname)) ':absolute))
(defun split-string (string regexp)
(coerce
(java:jcall (java:jmethod "java.lang.String" "split" "java.lang.String")
string regexp)
'list))
(defun path-separator ()
(java:jfield "java.io.File" "pathSeparator"))
(defun search-path-property (prop-name)
(let ((string (system-property prop-name)))
(and string
(remove nil
(mapcar #'truename
(split-string string (path-separator)))))))
(defun jdk-source-path ()
(let* ((jre-home (truename (system-property "java.home")))
(src-zip (merge-pathnames "src.zip" (pathname-parent jre-home)))
(truename (probe-file src-zip)))
(and truename (list truename))))
(defun class-path ()
(append (search-path-property "java.class.path")
(search-path-property "sun.boot.class.path")))
(defvar *source-path*
(append (search-path-property "user.dir")
(jdk-source-path)
;;(list (truename "/scratch/abcl/src"))
)
"List of directories to search for source files.")
(defun zipfile-contains-p (zipfile-name entry-name)
(let ((zipfile (java:jnew (java:jconstructor "java.util.zip.ZipFile"
"java.lang.String")
zipfile-name)))
(java:jcall
(java:jmethod "java.util.zip.ZipFile" "getEntry" "java.lang.String")
zipfile entry-name)))
;; (find-file-in-path "java/lang/String.java" *source-path*)
;; (find-file-in-path "Lisp.java" *source-path*)
;; Try to find FILENAME in PATH. If found, return a file spec as
;; needed by Emacs. We also look in zip files.
(defun find-file-in-path (filename path)
(labels ((try (dir)
(cond ((not (pathname-type dir))
(let ((f (probe-file (merge-pathnames filename dir))))
(and f `(:file ,(namestring f)))))
((equal (pathname-type dir) "zip")
(try-zip dir))
(t (error "strange path element: ~s" path))))
(try-zip (zip)
(let* ((zipfile-name (namestring (truename zip))))
(and (zipfile-contains-p zipfile-name filename)
`(:dir ,zipfile-name ,filename)))))
(cond ((pathname-absolute-p filename) (probe-file filename))
(t
(loop for dir in path
if (try dir) return it)))))
(defimplementation find-definitions (symbol)
(ext:resolve symbol)
(let ((srcloc (source-location symbol)))
(and srcloc `((,symbol ,srcloc)))))
#|
Uncomment this if you have patched xref.lisp, as in
http://article.gmane.org/gmane.lisp.slime.devel/2425
Also, make sure that xref.lisp is loaded by modifying the armedbear
part of *sysdep-pathnames* in swank.loader.lisp.
;;;; XREF
(setq pxref:*handle-package-forms* '(cl:in-package))
(defmacro defxref (name function)
`(defimplementation ,name (name)
(xref-results (,function name))))
(defxref who-calls pxref:list-callers)
(defxref who-references pxref:list-readers)
(defxref who-binds pxref:list-setters)
(defxref who-sets pxref:list-setters)
(defxref list-callers pxref:list-callers)
(defxref list-callees pxref:list-callees)
(defun xref-results (symbols)
(let ((xrefs '()))
(dolist (symbol symbols)
(push (list symbol (cadar (source-location symbol))) xrefs))
xrefs))
|#
;;;; Inspecting
(defmethod emacs-inspect ((o t))
(let ((parts (sys:inspected-parts o)))
`("The object is of type " ,(symbol-name (type-of o)) "." (:newline)
,@(if parts
(loop :for (label . value) :in parts
:appending (label-value-line label value))
(list "No inspectable parts, dumping output of CL:DESCRIBE:"
'(:newline)
(with-output-to-string (desc) (describe o desc)))))))
(defmethod emacs-inspect ((slot mop::slot-definition))
`("Name: "
(:value ,(mop:slot-definition-name slot))
(:newline)
"Documentation:" (:newline)
,@(when (slot-definition-documentation slot)
`((:value ,(slot-definition-documentation slot)) (:newline)))
"Initialization:" (:newline)
" Args: " (:value ,(mop:slot-definition-initargs slot)) (:newline)
" Form: " ,(if (mop:slot-definition-initfunction slot)
`(:value ,(mop:slot-definition-initform slot))
"#<unspecified>") (:newline)
" Function: "
(:value ,(mop:slot-definition-initfunction slot))
(:newline)))
(defmethod emacs-inspect ((f function))
`(,@(when (function-name f)
`("Name: "
,(princ-to-string (function-name f)) (:newline)))
,@(multiple-value-bind (args present)
(sys::arglist f)
(when present
`("Argument list: "
,(princ-to-string args) (:newline))))
(:newline)
#+nil,@(when (documentation f t)
`("Documentation:" (:newline)
,(documentation f t) (:newline)))
,@(when (function-lambda-expression f)
`("Lambda expression:"
(:newline) ,(princ-to-string
(function-lambda-expression f)) (:newline)))))
;;; Although by convention toString() is supposed to be a
;;; non-computationally expensive operation this isn't always the
;;; case, so make its computation a user interaction.
(defparameter *to-string-hashtable* (make-hash-table))
(defmethod emacs-inspect ((o java:java-object))
(let ((to-string (lambda ()
(handler-case
(setf (gethash o *to-string-hashtable*)
(java:jcall "toString" o))
(t (e)
(setf (gethash o *to-string-hashtable*)
(format nil
"Could not invoke toString(): ~A"
e)))))))
(append
(if (gethash o *to-string-hashtable*)
(label-value-line "toString()" (gethash o *to-string-hashtable*))
`((:action "[compute toString()]" ,to-string) (:newline)))
(loop :for (label . value) :in (sys:inspected-parts o)
:appending (label-value-line label value)))))
;;;; Multithreading
(defimplementation spawn (fn &key name)
(threads:make-thread (lambda () (funcall fn)) :name name))
(defvar *thread-plists* (make-hash-table) ; should be a weak table
"A hashtable mapping threads to a plist.")
(defvar *thread-id-counter* 0)
(defimplementation thread-id (thread)
(threads:synchronized-on *thread-plists*
(or (getf (gethash thread *thread-plists*) 'id)
(setf (getf (gethash thread *thread-plists*) 'id)
(incf *thread-id-counter*)))))
(defimplementation find-thread (id)
(find id (all-threads)
:key (lambda (thread)
(getf (gethash thread *thread-plists*) 'id))))
(defimplementation thread-name (thread)
(threads:thread-name thread))
(defimplementation thread-status (thread)
(format nil "Thread is ~:[dead~;alive~]" (threads:thread-alive-p thread)))
(defimplementation make-lock (&key name)
(declare (ignore name))
(threads:make-thread-lock))
(defimplementation call-with-lock-held (lock function)
(threads:with-thread-lock (lock) (funcall function)))
(defimplementation current-thread ()
(threads:current-thread))
(defimplementation all-threads ()
(copy-list (threads:mapcar-threads #'identity)))
(defimplementation thread-alive-p (thread)
(member thread (all-threads)))
(defimplementation interrupt-thread (thread fn)
(threads:interrupt-thread thread fn))
(defimplementation kill-thread (thread)
(threads:destroy-thread thread))
(defstruct mailbox
(queue '()))
(defun mailbox (thread)
"Return THREAD's mailbox."
(threads:synchronized-on *thread-plists*
(or (getf (gethash thread *thread-plists*) 'mailbox)
(setf (getf (gethash thread *thread-plists*) 'mailbox)
(make-mailbox)))))
(defimplementation send (thread message)
(let ((mbox (mailbox thread)))
(threads:synchronized-on mbox
(setf (mailbox-queue mbox)
(nconc (mailbox-queue mbox) (list message)))
(threads:object-notify-all mbox))))
(defimplementation receive-if (test &optional timeout)
(let* ((mbox (mailbox (current-thread))))
(assert (or (not timeout) (eq timeout t)))
(loop
(check-slime-interrupts)
(threads:synchronized-on mbox
(let* ((q (mailbox-queue mbox))
(tail (member-if test q)))
(when tail
(setf (mailbox-queue mbox) (nconc (ldiff q tail) (cdr tail)))
(return (car tail)))
(when (eq timeout t) (return (values nil t)))
(threads:object-wait mbox 0.3))))))
(defimplementation quit-lisp ()
(ext:exit))
;;;
#+#.(swank/backend:with-symbol 'package-local-nicknames 'ext)
(defimplementation package-local-nicknames (package)
(ext:package-local-nicknames package))