PageRenderTime 56ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/local-lisp/slime/swank-ecl.lisp

https://bitbucket.org/sakito/dot.emacs.d/
Lisp | 790 lines | 581 code | 123 blank | 86 comment | 14 complexity | 86e0e37d9f71aebfcce09e2b9815cbb7 MD5 | raw file
Possible License(s): GPL-3.0, CC-BY-SA-4.0, GPL-2.0, Unlicense
  1. ;;;; -*- indent-tabs-mode: nil -*-
  2. ;;;
  3. ;;; swank-ecl.lisp --- SLIME backend for ECL.
  4. ;;;
  5. ;;; This code has been placed in the Public Domain. All warranties
  6. ;;; are disclaimed.
  7. ;;;
  8. ;;; Administrivia
  9. (in-package :swank-backend)
  10. (eval-when (:compile-toplevel :load-toplevel :execute)
  11. (let ((version (find-symbol "+ECL-VERSION-NUMBER+" :EXT)))
  12. (when (or (not version) (< (symbol-value version) 100301))
  13. (error "~&IMPORTANT:~% ~
  14. The version of ECL you're using (~A) is too old.~% ~
  15. Please upgrade to at least 10.3.1.~% ~
  16. Sorry for the inconvenience.~%~%"
  17. (lisp-implementation-version)))))
  18. ;; Hard dependencies.
  19. (eval-when (:compile-toplevel :load-toplevel :execute)
  20. (require 'sockets))
  21. ;; Soft dependencies.
  22. (eval-when (:compile-toplevel :load-toplevel :execute)
  23. (when (probe-file "sys:profile.fas")
  24. (require :profile)
  25. (pushnew :profile *features*))
  26. (when (probe-file "sys:serve-event.fas")
  27. (require :serve-event)
  28. (pushnew :serve-event *features*)))
  29. (declaim (optimize (debug 3)))
  30. ;;; Swank-mop
  31. (eval-when (:compile-toplevel :load-toplevel :execute)
  32. (import-from :gray *gray-stream-symbols* :swank-backend)
  33. (import-swank-mop-symbols :clos
  34. '(:eql-specializer
  35. :eql-specializer-object
  36. :generic-function-declarations
  37. :specializer-direct-methods
  38. :compute-applicable-methods-using-classes)))
  39. ;;;; TCP Server
  40. (defimplementation preferred-communication-style ()
  41. ;; While ECL does provide threads, some parts of it are not
  42. ;; thread-safe (2010-02-23), including the compiler and CLOS.
  43. nil
  44. ;; ECL on Windows does not provide condition-variables
  45. ;; (or #+(and threads (not windows)) :spawn
  46. ;; nil)
  47. )
  48. (defun resolve-hostname (name)
  49. (car (sb-bsd-sockets:host-ent-addresses
  50. (sb-bsd-sockets:get-host-by-name name))))
  51. (defimplementation create-socket (host port)
  52. (let ((socket (make-instance 'sb-bsd-sockets:inet-socket
  53. :type :stream
  54. :protocol :tcp)))
  55. (setf (sb-bsd-sockets:sockopt-reuse-address socket) t)
  56. (sb-bsd-sockets:socket-bind socket (resolve-hostname host) port)
  57. (sb-bsd-sockets:socket-listen socket 5)
  58. socket))
  59. (defimplementation local-port (socket)
  60. (nth-value 1 (sb-bsd-sockets:socket-name socket)))
  61. (defimplementation close-socket (socket)
  62. (sb-bsd-sockets:socket-close socket))
  63. (defimplementation accept-connection (socket
  64. &key external-format
  65. buffering timeout)
  66. (declare (ignore timeout))
  67. (sb-bsd-sockets:socket-make-stream (accept socket)
  68. :output t
  69. :input t
  70. :buffering buffering
  71. :external-format external-format))
  72. (defun accept (socket)
  73. "Like socket-accept, but retry on EAGAIN."
  74. (loop (handler-case
  75. (return (sb-bsd-sockets:socket-accept socket))
  76. (sb-bsd-sockets:interrupted-error ()))))
  77. (defimplementation socket-fd (socket)
  78. (etypecase socket
  79. (fixnum socket)
  80. (two-way-stream (socket-fd (two-way-stream-input-stream socket)))
  81. (sb-bsd-sockets:socket (sb-bsd-sockets:socket-file-descriptor socket))
  82. (file-stream (si:file-stream-fd socket))))
  83. (defvar *external-format-to-coding-system*
  84. '((:latin-1
  85. "latin-1" "latin-1-unix" "iso-latin-1-unix"
  86. "iso-8859-1" "iso-8859-1-unix")
  87. (:utf-8 "utf-8" "utf-8-unix")))
  88. (defun external-format (coding-system)
  89. (or (car (rassoc-if (lambda (x) (member coding-system x :test #'equal))
  90. *external-format-to-coding-system*))
  91. (find coding-system (ext:all-encodings) :test #'string-equal)))
  92. (defimplementation find-external-format (coding-system)
  93. #+unicode (external-format coding-system)
  94. ;; Without unicode support, ECL uses the one-byte encoding of the
  95. ;; underlying OS, and will barf on anything except :DEFAULT. We
  96. ;; return NIL here for known multibyte encodings, so
  97. ;; SWANK:CREATE-SERVER will barf.
  98. #-unicode (let ((xf (external-format coding-system)))
  99. (if (member xf '(:utf-8))
  100. nil
  101. :default)))
  102. ;;;; Unix Integration
  103. ;;; If ECL is built with thread support, it'll spawn a helper thread
  104. ;;; executing the SIGINT handler. We do not want to BREAK into that
  105. ;;; helper but into the main thread, though. This is coupled with the
  106. ;;; current choice of NIL as communication-style in so far as ECL's
  107. ;;; main-thread is also the Slime's REPL thread.
  108. (defimplementation call-with-user-break-handler (real-handler function)
  109. (let ((old-handler #'si:terminal-interrupt))
  110. (setf (symbol-function 'si:terminal-interrupt)
  111. (make-interrupt-handler real-handler))
  112. (unwind-protect (funcall function)
  113. (setf (symbol-function 'si:terminal-interrupt) old-handler))))
  114. #+threads
  115. (defun make-interrupt-handler (real-handler)
  116. (let ((main-thread (find 'si:top-level (mp:all-processes)
  117. :key #'mp:process-name)))
  118. #'(lambda (&rest args)
  119. (declare (ignore args))
  120. (mp:interrupt-process main-thread real-handler))))
  121. #-threads
  122. (defun make-interrupt-handler (real-handler)
  123. #'(lambda (&rest args)
  124. (declare (ignore args))
  125. (funcall real-handler)))
  126. (defimplementation getpid ()
  127. (si:getpid))
  128. (defimplementation set-default-directory (directory)
  129. (ext:chdir (namestring directory)) ; adapts *DEFAULT-PATHNAME-DEFAULTS*.
  130. (default-directory))
  131. (defimplementation default-directory ()
  132. (namestring (ext:getcwd)))
  133. (defimplementation quit-lisp ()
  134. (ext:quit))
  135. ;;; Instead of busy waiting with communication-style NIL, use select()
  136. ;;; on the sockets' streams.
  137. #+serve-event
  138. (progn
  139. (defun poll-streams (streams timeout)
  140. (let* ((serve-event::*descriptor-handlers*
  141. (copy-list serve-event::*descriptor-handlers*))
  142. (active-fds '())
  143. (fd-stream-alist
  144. (loop for s in streams
  145. for fd = (socket-fd s)
  146. collect (cons fd s)
  147. do (serve-event:add-fd-handler fd :input
  148. #'(lambda (fd)
  149. (push fd active-fds))))))
  150. (serve-event:serve-event timeout)
  151. (loop for fd in active-fds collect (cdr (assoc fd fd-stream-alist)))))
  152. (defimplementation wait-for-input (streams &optional timeout)
  153. (assert (member timeout '(nil t)))
  154. (loop
  155. (cond ((check-slime-interrupts) (return :interrupt))
  156. (timeout (return (poll-streams streams 0)))
  157. (t
  158. (when-let (ready (poll-streams streams 0.2))
  159. (return ready))))))
  160. ) ; #+serve-event (progn ...
  161. ;;;; Compilation
  162. (defvar *buffer-name* nil)
  163. (defvar *buffer-start-position*)
  164. (defun signal-compiler-condition (&rest args)
  165. (signal (apply #'make-condition 'compiler-condition args)))
  166. (defun handle-compiler-message (condition)
  167. ;; ECL emits lots of noise in compiler-notes, like "Invoking
  168. ;; external command".
  169. (unless (typep condition 'c::compiler-note)
  170. (signal-compiler-condition
  171. :original-condition condition
  172. :message (princ-to-string condition)
  173. :severity (etypecase condition
  174. (c:compiler-fatal-error :error)
  175. (c:compiler-error :error)
  176. (error :error)
  177. (style-warning :style-warning)
  178. (warning :warning))
  179. :location (condition-location condition))))
  180. (defun condition-location (condition)
  181. (let ((file (c:compiler-message-file condition))
  182. (position (c:compiler-message-file-position condition)))
  183. (if (and position (not (minusp position)))
  184. (if *buffer-name*
  185. (make-buffer-location *buffer-name*
  186. *buffer-start-position*
  187. position)
  188. (make-file-location file position))
  189. (make-error-location "No location found."))))
  190. (defimplementation call-with-compilation-hooks (function)
  191. (handler-bind ((c:compiler-message #'handle-compiler-message))
  192. (funcall function)))
  193. (defimplementation swank-compile-file (input-file output-file
  194. load-p external-format
  195. &key policy)
  196. (declare (ignore policy))
  197. (with-compilation-hooks ()
  198. (compile-file input-file :output-file output-file
  199. :load load-p
  200. :external-format external-format)))
  201. (defvar *tmpfile-map* (make-hash-table :test #'equal))
  202. (defun note-buffer-tmpfile (tmp-file buffer-name)
  203. ;; EXT:COMPILED-FUNCTION-FILE below will return a namestring.
  204. (let ((tmp-namestring (namestring (truename tmp-file))))
  205. (setf (gethash tmp-namestring *tmpfile-map*) buffer-name)
  206. tmp-namestring))
  207. (defun tmpfile-to-buffer (tmp-file)
  208. (gethash tmp-file *tmpfile-map*))
  209. (defimplementation swank-compile-string (string &key buffer position filename
  210. policy)
  211. (declare (ignore policy))
  212. (with-compilation-hooks ()
  213. (let ((*buffer-name* buffer) ; for compilation hooks
  214. (*buffer-start-position* position))
  215. (let ((tmp-file (si:mkstemp "TMP:ecl-swank-tmpfile-"))
  216. (fasl-file)
  217. (warnings-p)
  218. (failure-p))
  219. (unwind-protect
  220. (with-open-file (tmp-stream tmp-file :direction :output
  221. :if-exists :supersede)
  222. (write-string string tmp-stream)
  223. (finish-output tmp-stream)
  224. (multiple-value-setq (fasl-file warnings-p failure-p)
  225. (compile-file tmp-file
  226. :load t
  227. :source-truename (or filename
  228. (note-buffer-tmpfile tmp-file buffer))
  229. :source-offset (1- position))))
  230. (when (probe-file tmp-file)
  231. (delete-file tmp-file))
  232. (when fasl-file
  233. (delete-file fasl-file)))
  234. (not failure-p)))))
  235. ;;;; Documentation
  236. (defimplementation arglist (name)
  237. (multiple-value-bind (arglist foundp)
  238. (ext:function-lambda-list name)
  239. (if foundp arglist :not-available)))
  240. (defimplementation function-name (f)
  241. (typecase f
  242. (generic-function (clos:generic-function-name f))
  243. (function (si:compiled-function-name f))))
  244. ;; FIXME
  245. ;; (defimplementation macroexpand-all (form))
  246. (defimplementation describe-symbol-for-emacs (symbol)
  247. (let ((result '()))
  248. (dolist (type '(:VARIABLE :FUNCTION :CLASS))
  249. (when-let (doc (describe-definition symbol type))
  250. (setf result (list* type doc result))))
  251. result))
  252. (defimplementation describe-definition (name type)
  253. (case type
  254. (:variable (documentation name 'variable))
  255. (:function (documentation name 'function))
  256. (:class (documentation name 'class))
  257. (t nil)))
  258. ;;; Debugging
  259. (eval-when (:compile-toplevel :load-toplevel :execute)
  260. (import
  261. '(si::*break-env*
  262. si::*ihs-top*
  263. si::*ihs-current*
  264. si::*ihs-base*
  265. si::*frs-base*
  266. si::*frs-top*
  267. si::*tpl-commands*
  268. si::*tpl-level*
  269. si::frs-top
  270. si::ihs-top
  271. si::ihs-fun
  272. si::ihs-env
  273. si::sch-frs-base
  274. si::set-break-env
  275. si::set-current-ihs
  276. si::tpl-commands)))
  277. (defun make-invoke-debugger-hook (hook)
  278. (when hook
  279. #'(lambda (condition old-hook)
  280. ;; Regard *debugger-hook* if set by user.
  281. (if *debugger-hook*
  282. nil ; decline, *DEBUGGER-HOOK* will be tried next.
  283. (funcall hook condition old-hook)))))
  284. (defimplementation install-debugger-globally (function)
  285. (setq *debugger-hook* function)
  286. (setq ext:*invoke-debugger-hook* (make-invoke-debugger-hook function)))
  287. (defimplementation call-with-debugger-hook (hook fun)
  288. (let ((*debugger-hook* hook)
  289. (ext:*invoke-debugger-hook* (make-invoke-debugger-hook hook)))
  290. (funcall fun)))
  291. (defvar *backtrace* '())
  292. ;;; Commented out; it's not clear this is a good way of doing it. In
  293. ;;; particular because it makes errors stemming from this file harder
  294. ;;; to debug, and given the "young" age of ECL's swank backend, that's
  295. ;;; a bad idea.
  296. ;; (defun in-swank-package-p (x)
  297. ;; (and
  298. ;; (symbolp x)
  299. ;; (member (symbol-package x)
  300. ;; (list #.(find-package :swank)
  301. ;; #.(find-package :swank-backend)
  302. ;; #.(ignore-errors (find-package :swank-mop))
  303. ;; #.(ignore-errors (find-package :swank-loader))))
  304. ;; t))
  305. ;; (defun is-swank-source-p (name)
  306. ;; (setf name (pathname name))
  307. ;; (pathname-match-p
  308. ;; name
  309. ;; (make-pathname :defaults swank-loader::*source-directory*
  310. ;; :name (pathname-name name)
  311. ;; :type (pathname-type name)
  312. ;; :version (pathname-version name))))
  313. ;; (defun is-ignorable-fun-p (x)
  314. ;; (or
  315. ;; (in-swank-package-p (frame-name x))
  316. ;; (multiple-value-bind (file position)
  317. ;; (ignore-errors (si::bc-file (car x)))
  318. ;; (declare (ignore position))
  319. ;; (if file (is-swank-source-p file)))))
  320. (defimplementation call-with-debugging-environment (debugger-loop-fn)
  321. (declare (type function debugger-loop-fn))
  322. (let* ((*ihs-top* (ihs-top))
  323. (*ihs-current* *ihs-top*)
  324. (*frs-base* (or (sch-frs-base *frs-top* *ihs-base*) (1+ (frs-top))))
  325. (*frs-top* (frs-top))
  326. (*tpl-level* (1+ *tpl-level*))
  327. (*backtrace* (loop for ihs from 0 below *ihs-top*
  328. collect (list (si::ihs-fun ihs)
  329. (si::ihs-env ihs)
  330. nil))))
  331. (declare (special *ihs-current*))
  332. (loop for f from *frs-base* until *frs-top*
  333. do (let ((i (- (si::frs-ihs f) *ihs-base* 1)))
  334. (when (plusp i)
  335. (let* ((x (elt *backtrace* i))
  336. (name (si::frs-tag f)))
  337. (unless (si::fixnump name)
  338. (push name (third x)))))))
  339. (setf *backtrace* (nreverse *backtrace*))
  340. (set-break-env)
  341. (set-current-ihs)
  342. (let ((*ihs-base* *ihs-top*))
  343. (funcall debugger-loop-fn))))
  344. (defimplementation compute-backtrace (start end)
  345. (when (numberp end)
  346. (setf end (min end (length *backtrace*))))
  347. (loop for f in (subseq *backtrace* start end)
  348. collect f))
  349. (defun frame-name (frame)
  350. (let ((x (first frame)))
  351. (if (symbolp x)
  352. x
  353. (function-name x))))
  354. (defun function-position (fun)
  355. (multiple-value-bind (file position)
  356. (si::bc-file fun)
  357. (when file
  358. (make-file-location file position))))
  359. (defun frame-function (frame)
  360. (let* ((x (first frame))
  361. fun position)
  362. (etypecase x
  363. (symbol (and (fboundp x)
  364. (setf fun (fdefinition x)
  365. position (function-position fun))))
  366. (function (setf fun x position (function-position x))))
  367. (values fun position)))
  368. (defun frame-decode-env (frame)
  369. (let ((functions '())
  370. (blocks '())
  371. (variables '()))
  372. (setf frame (si::decode-ihs-env (second frame)))
  373. (dolist (record (remove-if-not #'consp frame))
  374. (let* ((record0 (car record))
  375. (record1 (cdr record)))
  376. (cond ((or (symbolp record0) (stringp record0))
  377. (setq variables (acons record0 record1 variables)))
  378. ((not (si::fixnump record0))
  379. (push record1 functions))
  380. ((symbolp record1)
  381. (push record1 blocks))
  382. (t
  383. ))))
  384. (values functions blocks variables)))
  385. (defimplementation print-frame (frame stream)
  386. (format stream "~A" (first frame)))
  387. (defimplementation frame-source-location (frame-number)
  388. (nth-value 1 (frame-function (elt *backtrace* frame-number))))
  389. (defimplementation frame-catch-tags (frame-number)
  390. (third (elt *backtrace* frame-number)))
  391. (defimplementation frame-locals (frame-number)
  392. (loop for (name . value) in (nth-value 2 (frame-decode-env (elt *backtrace* frame-number)))
  393. with i = 0
  394. collect (list :name name :id (prog1 i (incf i)) :value value)))
  395. (defimplementation frame-var-value (frame-number var-id)
  396. (elt (nth-value 2 (frame-decode-env (elt *backtrace* frame-number)))
  397. var-id))
  398. (defimplementation disassemble-frame (frame-number)
  399. (let ((fun (frame-function (elt *backtrace* frame-number))))
  400. (disassemble fun)))
  401. (defimplementation eval-in-frame (form frame-number)
  402. (let ((env (second (elt *backtrace* frame-number))))
  403. (si:eval-with-env form env)))
  404. (defimplementation gdb-initial-commands ()
  405. ;; These signals are used by the GC.
  406. #+linux '("handle SIGPWR noprint nostop"
  407. "handle SIGXCPU noprint nostop"))
  408. (defimplementation command-line-args ()
  409. (loop for n from 0 below (si:argc) collect (si:argv n)))
  410. ;;;; Inspector
  411. ;;; FIXME: Would be nice if it was possible to inspect objects
  412. ;;; implemented in C.
  413. ;;;; Definitions
  414. (defvar +TAGS+ (namestring (translate-logical-pathname #P"SYS:TAGS")))
  415. (defun make-file-location (file file-position)
  416. ;; File positions in CL start at 0, but Emacs' buffer positions
  417. ;; start at 1. We specify (:ALIGN T) because the positions comming
  418. ;; from ECL point at right after the toplevel form appearing before
  419. ;; the actual target toplevel form; (:ALIGN T) will DTRT in that case.
  420. (make-location `(:file ,(namestring (translate-logical-pathname file)))
  421. `(:position ,(1+ file-position))
  422. `(:align t)))
  423. (defun make-buffer-location (buffer-name start-position &optional (offset 0))
  424. (make-location `(:buffer ,buffer-name)
  425. `(:offset ,start-position ,offset)
  426. `(:align t)))
  427. (defun make-TAGS-location (&rest tags)
  428. (make-location `(:etags-file ,+TAGS+)
  429. `(:tag ,@tags)))
  430. (defimplementation find-definitions (name)
  431. (let ((annotations (ext:get-annotation name 'si::location :all)))
  432. (cond (annotations
  433. (loop for annotation in annotations
  434. collect (destructuring-bind (dspec file . pos) annotation
  435. `(,dspec ,(make-file-location file pos)))))
  436. (t
  437. (mapcan #'(lambda (type) (find-definitions-by-type name type))
  438. (classify-definition-name name))))))
  439. (defun classify-definition-name (name)
  440. (let ((types '()))
  441. (when (fboundp name)
  442. (cond ((special-operator-p name)
  443. (push :special-operator types))
  444. ((macro-function name)
  445. (push :macro types))
  446. ((typep (fdefinition name) 'generic-function)
  447. (push :generic-function types))
  448. ((si:mangle-name name t)
  449. (push :c-function types))
  450. (t
  451. (push :lisp-function types))))
  452. (when (boundp name)
  453. (cond ((constantp name)
  454. (push :constant types))
  455. (t
  456. (push :global-variable types))))
  457. types))
  458. (defun find-definitions-by-type (name type)
  459. (ecase type
  460. (:lisp-function
  461. (when-let (loc (source-location (fdefinition name)))
  462. (list `((defun ,name) ,loc))))
  463. (:c-function
  464. (when-let (loc (source-location (fdefinition name)))
  465. (list `((c-source ,name) ,loc))))
  466. (:generic-function
  467. (loop for method in (clos:generic-function-methods (fdefinition name))
  468. for specs = (clos:method-specializers method)
  469. for loc = (source-location method)
  470. when loc
  471. collect `((defmethod ,name ,specs) ,loc)))
  472. (:macro
  473. (when-let (loc (source-location (macro-function name)))
  474. (list `((defmacro ,name) ,loc))))
  475. (:constant
  476. (when-let (loc (source-location name))
  477. (list `((defconstant ,name) ,loc))))
  478. (:global-variable
  479. (when-let (loc (source-location name))
  480. (list `((defvar ,name) ,loc))))
  481. (:special-operator)))
  482. ;;; FIXME: There ought to be a better way.
  483. (eval-when (:compile-toplevel :load-toplevel :execute)
  484. (defun c-function-name-p (name)
  485. (and (symbolp name) (si:mangle-name name t) t))
  486. (defun c-function-p (object)
  487. (and (functionp object)
  488. (let ((fn-name (function-name object)))
  489. (and fn-name (c-function-name-p fn-name))))))
  490. (deftype c-function ()
  491. `(satisfies c-function-p))
  492. (defun assert-source-directory ()
  493. (unless (probe-file #P"SRC:")
  494. (error "ECL's source directory ~A does not exist. ~
  495. You can specify a different location via the environment ~
  496. variable `ECLSRCDIR'."
  497. (namestring (translate-logical-pathname #P"SYS:")))))
  498. (defun assert-TAGS-file ()
  499. (unless (probe-file +TAGS+)
  500. (error "No TAGS file ~A found. It should have been installed with ECL."
  501. +TAGS+)))
  502. (defun package-names (package)
  503. (cons (package-name package) (package-nicknames package)))
  504. (defun source-location (object)
  505. (converting-errors-to-error-location
  506. (typecase object
  507. (c-function
  508. (assert-source-directory)
  509. (assert-TAGS-file)
  510. (let ((lisp-name (function-name object)))
  511. (assert lisp-name)
  512. (multiple-value-bind (flag c-name) (si:mangle-name lisp-name t)
  513. (assert flag)
  514. ;; In ECL's code base sometimes the mangled name is used
  515. ;; directly, sometimes ECL's DPP magic of @SI::SYMBOL or
  516. ;; @EXT::SYMBOL is used. We cannot predict here, so we just
  517. ;; provide several candidates.
  518. (apply #'make-TAGS-location
  519. c-name
  520. (loop with s = (symbol-name lisp-name)
  521. for p in (package-names (symbol-package lisp-name))
  522. collect (format nil "~A::~A" p s)
  523. collect (format nil "~(~A::~A~)" p s))))))
  524. (function
  525. (multiple-value-bind (file pos) (ext:compiled-function-file object)
  526. (cond ((not file)
  527. (return-from source-location nil))
  528. ((tmpfile-to-buffer file)
  529. (make-buffer-location (tmpfile-to-buffer file) pos))
  530. (t
  531. (assert (probe-file file))
  532. (assert (not (minusp pos)))
  533. (make-file-location file pos)))))
  534. (method
  535. ;; FIXME: This will always return NIL at the moment; ECL does not
  536. ;; store debug information for methods yet.
  537. (source-location (clos:method-function object)))
  538. ((member nil t)
  539. (multiple-value-bind (flag c-name) (si:mangle-name object)
  540. (assert flag)
  541. (make-TAGS-location c-name))))))
  542. (defimplementation find-source-location (object)
  543. (or (source-location object)
  544. (make-error-location "Source definition of ~S not found." object)))
  545. ;;;; Profiling
  546. #+profile
  547. (progn
  548. (defimplementation profile (fname)
  549. (when fname (eval `(profile:profile ,fname))))
  550. (defimplementation unprofile (fname)
  551. (when fname (eval `(profile:unprofile ,fname))))
  552. (defimplementation unprofile-all ()
  553. (profile:unprofile-all)
  554. "All functions unprofiled.")
  555. (defimplementation profile-report ()
  556. (profile:report))
  557. (defimplementation profile-reset ()
  558. (profile:reset)
  559. "Reset profiling counters.")
  560. (defimplementation profiled-functions ()
  561. (profile:profile))
  562. (defimplementation profile-package (package callers methods)
  563. (declare (ignore callers methods))
  564. (eval `(profile:profile ,(package-name (find-package package)))))
  565. ) ; #+profile (progn ...
  566. ;;;; Threads
  567. #+threads
  568. (progn
  569. (defvar *thread-id-counter* 0)
  570. (defparameter *thread-id-map* (make-hash-table))
  571. (defvar *thread-id-map-lock*
  572. (mp:make-lock :name "thread id map lock"))
  573. (defimplementation spawn (fn &key name)
  574. (mp:process-run-function name fn))
  575. (defimplementation thread-id (target-thread)
  576. (block thread-id
  577. (mp:with-lock (*thread-id-map-lock*)
  578. ;; Does TARGET-THREAD have an id already?
  579. (maphash (lambda (id thread-pointer)
  580. (let ((thread (si:weak-pointer-value thread-pointer)))
  581. (cond ((not thread)
  582. (remhash id *thread-id-map*))
  583. ((eq thread target-thread)
  584. (return-from thread-id id)))))
  585. *thread-id-map*)
  586. ;; TARGET-THREAD not found in *THREAD-ID-MAP*
  587. (let ((id (incf *thread-id-counter*))
  588. (thread-pointer (si:make-weak-pointer target-thread)))
  589. (setf (gethash id *thread-id-map*) thread-pointer)
  590. id))))
  591. (defimplementation find-thread (id)
  592. (mp:with-lock (*thread-id-map-lock*)
  593. (let* ((thread-ptr (gethash id *thread-id-map*))
  594. (thread (and thread-ptr (si:weak-pointer-value thread-ptr))))
  595. (unless thread
  596. (remhash id *thread-id-map*))
  597. thread)))
  598. (defimplementation thread-name (thread)
  599. (mp:process-name thread))
  600. (defimplementation thread-status (thread)
  601. (if (mp:process-active-p thread)
  602. "RUNNING"
  603. "STOPPED"))
  604. (defimplementation make-lock (&key name)
  605. (mp:make-lock :name name))
  606. (defimplementation call-with-lock-held (lock function)
  607. (declare (type function function))
  608. (mp:with-lock (lock) (funcall function)))
  609. (defimplementation current-thread ()
  610. mp:*current-process*)
  611. (defimplementation all-threads ()
  612. (mp:all-processes))
  613. (defimplementation interrupt-thread (thread fn)
  614. (mp:interrupt-process thread fn))
  615. (defimplementation kill-thread (thread)
  616. (mp:process-kill thread))
  617. (defimplementation thread-alive-p (thread)
  618. (mp:process-active-p thread))
  619. (defvar *mailbox-lock* (mp:make-lock :name "mailbox lock"))
  620. (defvar *mailboxes* (list))
  621. (declaim (type list *mailboxes*))
  622. (defstruct (mailbox (:conc-name mailbox.))
  623. thread
  624. (mutex (mp:make-lock))
  625. (cvar (mp:make-condition-variable))
  626. (queue '() :type list))
  627. (defun mailbox (thread)
  628. "Return THREAD's mailbox."
  629. (mp:with-lock (*mailbox-lock*)
  630. (or (find thread *mailboxes* :key #'mailbox.thread)
  631. (let ((mb (make-mailbox :thread thread)))
  632. (push mb *mailboxes*)
  633. mb))))
  634. (defimplementation send (thread message)
  635. (let* ((mbox (mailbox thread))
  636. (mutex (mailbox.mutex mbox)))
  637. (mp:with-lock (mutex)
  638. (setf (mailbox.queue mbox)
  639. (nconc (mailbox.queue mbox) (list message)))
  640. (mp:condition-variable-broadcast (mailbox.cvar mbox)))))
  641. (defimplementation receive-if (test &optional timeout)
  642. (let* ((mbox (mailbox (current-thread)))
  643. (mutex (mailbox.mutex mbox)))
  644. (assert (or (not timeout) (eq timeout t)))
  645. (loop
  646. (check-slime-interrupts)
  647. (mp:with-lock (mutex)
  648. (let* ((q (mailbox.queue mbox))
  649. (tail (member-if test q)))
  650. (when tail
  651. (setf (mailbox.queue mbox) (nconc (ldiff q tail) (cdr tail)))
  652. (return (car tail))))
  653. (when (eq timeout t) (return (values nil t)))
  654. (mp:condition-variable-timedwait (mailbox.cvar mbox)
  655. mutex
  656. 0.2)))))
  657. ) ; #+threads (progn ...