mkcl.lisp (31123B)
1 ;;;; -*- indent-tabs-mode: nil -*- 2 ;;; 3 ;;; slynk-mkcl.lisp --- SLIME backend for MKCL. 4 ;;; 5 ;;; This code has been placed in the Public Domain. All warranties 6 ;;; are disclaimed. 7 ;;; 8 9 ;;; Administrivia 10 11 (defpackage slynk-mkcl 12 (:use cl slynk-backend)) 13 14 (in-package slynk-mkcl) 15 16 ;;(declaim (optimize (debug 3))) 17 18 (defvar *tmp*) 19 20 (defimplementation gray-package-name () 21 '#:gray) 22 23 (eval-when (:compile-toplevel :load-toplevel) 24 25 (slynk-backend::import-slynk-mop-symbols :clos 26 ;; '(:eql-specializer 27 ;; :eql-specializer-object 28 ;; :generic-function-declarations 29 ;; :specializer-direct-methods 30 ;; :compute-applicable-methods-using-classes) 31 nil 32 )) 33 34 35 ;;; UTF8 36 37 (defimplementation string-to-utf8 (string) 38 (mkcl:octets (si:utf-8 string))) 39 40 (defimplementation utf8-to-string (octets) 41 (string (si:utf-8 octets))) 42 43 44 ;;;; TCP Server 45 46 (eval-when (:compile-toplevel :load-toplevel) 47 ;; At compile-time we need access to the sb-bsd-sockets package for the 48 ;; the following code to be read properly. 49 ;; It is a bit a shame we have to load the entire module to get that. 50 (require 'sockets)) 51 52 53 (defun resolve-hostname (name) 54 (car (sb-bsd-sockets:host-ent-addresses 55 (sb-bsd-sockets:get-host-by-name name)))) 56 57 (defimplementation create-socket (host port &key backlog) 58 (let ((socket (make-instance 'sb-bsd-sockets:inet-socket 59 :type :stream 60 :protocol :tcp))) 61 (setf (sb-bsd-sockets:sockopt-reuse-address socket) t) 62 (sb-bsd-sockets:socket-bind socket (resolve-hostname host) port) 63 (sb-bsd-sockets:socket-listen socket (or backlog 5)) 64 socket)) 65 66 (defimplementation local-port (socket) 67 (nth-value 1 (sb-bsd-sockets:socket-name socket))) 68 69 (defimplementation close-socket (socket) 70 (sb-bsd-sockets:socket-close socket)) 71 72 (defun accept (socket) 73 "Like socket-accept, but retry on EINTR." 74 (loop (handler-case 75 (return (sb-bsd-sockets:socket-accept socket)) 76 (sb-bsd-sockets:interrupted-error ())))) 77 78 (defimplementation accept-connection (socket 79 &key external-format 80 buffering timeout) 81 (declare (ignore timeout)) 82 (sb-bsd-sockets:socket-make-stream (accept socket) 83 :output t ;; bogus 84 :input t ;; bogus 85 :buffering buffering ;; bogus 86 :element-type (if external-format 87 'character 88 '(unsigned-byte 8)) 89 :external-format external-format 90 )) 91 92 (defimplementation preferred-communication-style () 93 :spawn 94 ) 95 96 (defvar *external-format-to-coding-system* 97 '((:iso-8859-1 98 "latin-1" "latin-1-unix" "iso-latin-1-unix" 99 "iso-8859-1" "iso-8859-1-unix") 100 (:utf-8 "utf-8" "utf-8-unix"))) 101 102 (defun external-format (coding-system) 103 (or (car (rassoc-if (lambda (x) (member coding-system x :test #'equal)) 104 *external-format-to-coding-system*)) 105 (find coding-system (si:all-encodings) :test #'string-equal))) 106 107 (defimplementation find-external-format (coding-system) 108 #+unicode (external-format coding-system) 109 ;; Without unicode support, MKCL uses the one-byte encoding of the 110 ;; underlying OS, and will barf on anything except :DEFAULT. We 111 ;; return NIL here for known multibyte encodings, so 112 ;; SLYNK:CREATE-SERVER will barf. 113 #-unicode (let ((xf (external-format coding-system))) 114 (if (member xf '(:utf-8)) 115 nil 116 :default))) 117 118 119 120 ;;;; Unix signals 121 122 (defimplementation install-sigint-handler (handler) 123 (let ((old-handler (symbol-function 'si:terminal-interrupt))) 124 (setf (symbol-function 'si:terminal-interrupt) 125 (if (consp handler) 126 (car handler) 127 (lambda (&rest args) 128 (declare (ignore args)) 129 (funcall handler) 130 (continue)))) 131 (list old-handler))) 132 133 134 (defimplementation getpid () 135 (mkcl:getpid)) 136 137 (defimplementation set-default-directory (directory) 138 (mk-ext::chdir (namestring directory)) 139 (default-directory)) 140 141 (defimplementation default-directory () 142 (namestring (mk-ext:getcwd))) 143 144 (defmacro progf (plist &rest forms) 145 `(let (_vars _vals) 146 (do ((p ,plist (cddr p))) 147 ((endp p)) 148 (push (car p) _vars) 149 (push (cadr p) _vals)) 150 (progv _vars _vals ,@forms) 151 ) 152 ) 153 154 (defvar *inferior-lisp-sleeping-post* nil) 155 156 (defimplementation quit-lisp () 157 ;; restore original IO streams. 158 (progf (ignore-errors (eval 159 (slynk-backend:find-symbol2 "slynk::*saved-global-streams*"))) 160 (when *inferior-lisp-sleeping-post* (mt:semaphore-signal *inferior-lisp-sleeping-post*)) 161 ;;(mk-ext:quit :verbose t) 162 )) 163 164 165 ;;;; Compilation 166 167 (defvar *buffer-name* nil) 168 (defvar *buffer-start-position*) 169 (defvar *buffer-string*) 170 (defvar *compile-filename*) 171 172 (defun signal-compiler-condition (&rest args) 173 (signal (apply #'make-condition 'compiler-condition args))) 174 175 #| 176 (defun handle-compiler-warning (condition) 177 (signal-compiler-condition 178 :original-condition condition 179 :message (format nil "~A" condition) 180 :severity :warning 181 :location 182 (if *buffer-name* 183 (make-location (list :buffer *buffer-name*) 184 (list :offset *buffer-start-position* 0)) 185 ;; ;; compiler::*current-form* 186 ;; (if compiler::*current-function* 187 ;; (make-location (list :file *compile-filename*) 188 ;; (list :function-name 189 ;; (symbol-name 190 ;; (slot-value compiler::*current-function* 191 ;; 'compiler::name)))) 192 (list :error "No location found.") 193 ;; ) 194 ))) 195 |# 196 197 #| 198 (defun condition-location (condition) 199 (let ((file (compiler:compiler-message-file condition)) 200 (position (compiler:compiler-message-file-position condition))) 201 (if (and position (not (minusp position))) 202 (if *buffer-name* 203 (make-buffer-location *buffer-name* 204 *buffer-start-position* 205 position) 206 (make-file-location file position)) 207 (make-error-location "No location found.")))) 208 |# 209 210 (defun condition-location (condition) 211 (if *buffer-name* 212 (make-location (list :buffer *buffer-name*) 213 (list :offset *buffer-start-position* 0)) 214 ;; ;; compiler::*current-form* ; 215 ;; (if compiler::*current-function* ; 216 ;; (make-location (list :file *compile-filename*) ; 217 ;; (list :function-name ; 218 ;; (symbol-name ; 219 ;; (slot-value compiler::*current-function* ; 220 ;; 'compiler::name)))) ; 221 (if (typep condition 'compiler::compiler-message) 222 (make-location (list :file (namestring (compiler:compiler-message-file condition))) 223 (list :end-position (compiler:compiler-message-file-end-position condition))) 224 (list :error "No location found.")) 225 ) 226 ) 227 228 (defun handle-compiler-message (condition) 229 (unless (typep condition 'compiler::compiler-note) 230 (signal-compiler-condition 231 :original-condition condition 232 :message (princ-to-string condition) 233 :severity (etypecase condition 234 (compiler:compiler-fatal-error :error) 235 (compiler:compiler-error :error) 236 (error :error) 237 (style-warning :style-warning) 238 (warning :warning)) 239 :location (condition-location condition)))) 240 241 (defimplementation call-with-compilation-hooks (function) 242 (handler-bind ((compiler:compiler-message #'handle-compiler-message)) 243 (funcall function))) 244 245 (defimplementation slynk-compile-file (input-file output-file 246 load-p external-format 247 &key policy) 248 (declare (ignore policy)) 249 (with-compilation-hooks () 250 (let ((*buffer-name* nil) 251 (*compile-filename* input-file)) 252 (handler-bind (#| 253 (compiler::compiler-note 254 #'(lambda (n) 255 (format t "~%slynk saw a compiler note: ~A~%" n) (finish-output) nil)) 256 (compiler::compiler-warning 257 #'(lambda (w) 258 (format t "~%slynk saw a compiler warning: ~A~%" w) (finish-output) nil)) 259 (compiler::compiler-error 260 #'(lambda (e) 261 (format t "~%slynk saw a compiler error: ~A~%" e) (finish-output) nil)) 262 |# 263 ) 264 (multiple-value-bind (output-truename warnings-p failure-p) 265 (compile-file input-file :output-file output-file :external-format external-format) 266 (values output-truename warnings-p 267 (or failure-p 268 (and load-p (not (load output-truename)))))))))) 269 270 (defimplementation slynk-compile-string (string &key buffer position filename line column policy) 271 (declare (ignore filename line column policy)) 272 (with-compilation-hooks () 273 (let ((*buffer-name* buffer) 274 (*buffer-start-position* position) 275 (*buffer-string* string)) 276 (with-input-from-string (s string) 277 (when position (file-position position)) 278 (compile-from-stream s))))) 279 280 (defun compile-from-stream (stream) 281 (let ((file (mkcl:mkstemp "TMP:MKCL-SLYNK-TMPXXXXXX")) 282 output-truename 283 warnings-p 284 failure-p 285 ) 286 (with-open-file (s file :direction :output :if-exists :overwrite) 287 (do ((line (read-line stream nil) (read-line stream nil))) 288 ((not line)) 289 (write-line line s))) 290 (unwind-protect 291 (progn 292 (multiple-value-setq (output-truename warnings-p failure-p) 293 (compile-file file)) 294 (and (not failure-p) (load output-truename))) 295 (when (probe-file file) (delete-file file)) 296 (when (probe-file output-truename) (delete-file output-truename))))) 297 298 299 ;;;; Documentation 300 301 (defun grovel-docstring-for-arglist (name type) 302 (flet ((compute-arglist-offset (docstring) 303 (when docstring 304 (let ((pos1 (search "Args: " docstring))) 305 (if pos1 306 (+ pos1 6) 307 (let ((pos2 (search "Syntax: " docstring))) 308 (when pos2 309 (+ pos2 8)))))))) 310 (let* ((docstring (si::get-documentation name type)) 311 (pos (compute-arglist-offset docstring))) 312 (if pos 313 (multiple-value-bind (arglist errorp) 314 (ignore-errors 315 (values (read-from-string docstring t nil :start pos))) 316 (if (or errorp (not (listp arglist))) 317 :not-available 318 arglist 319 )) 320 :not-available )))) 321 322 (defimplementation arglist (name) 323 (cond ((and (symbolp name) (special-operator-p name)) 324 (let ((arglist (grovel-docstring-for-arglist name 'function))) 325 (if (consp arglist) (cdr arglist) arglist))) 326 ((and (symbolp name) (macro-function name)) 327 (let ((arglist (grovel-docstring-for-arglist name 'function))) 328 (if (consp arglist) (cdr arglist) arglist))) 329 ((or (functionp name) (fboundp name)) 330 (multiple-value-bind (name fndef) 331 (if (functionp name) 332 (values (function-name name) name) 333 (values name (fdefinition name))) 334 (let ((fle (function-lambda-expression fndef))) 335 (case (car fle) 336 (si:lambda-block (caddr fle)) 337 (t (typecase fndef 338 (generic-function (clos::generic-function-lambda-list fndef)) 339 (compiled-function (grovel-docstring-for-arglist name 'function)) 340 (function :not-available))))))) 341 (t :not-available))) 342 343 (defimplementation function-name (f) 344 (si:compiled-function-name f) 345 ) 346 347 (eval-when (:compile-toplevel :load-toplevel) 348 ;; At compile-time we need access to the walker package for the 349 ;; the following code to be read properly. 350 ;; It is a bit a shame we have to load the entire module to get that. 351 (require 'walker)) 352 353 (defimplementation macroexpand-all (form &optional env) 354 (declare (ignore env)) 355 (walker:macroexpand-all form)) 356 357 (defimplementation describe-symbol-for-emacs (symbol) 358 (let ((result '())) 359 (dolist (type '(:VARIABLE :FUNCTION :CLASS)) 360 (let ((doc (describe-definition symbol type))) 361 (when doc 362 (setf result (list* type doc result))))) 363 result)) 364 365 (defimplementation describe-definition (name type) 366 (case type 367 (:variable (documentation name 'variable)) 368 (:function (documentation name 'function)) 369 (:class (documentation name 'class)) 370 (t nil))) 371 372 ;;; Debugging 373 374 (eval-when (:compile-toplevel :load-toplevel) 375 (import 376 '(si::*break-env* 377 si::*ihs-top* 378 si::*ihs-current* 379 si::*ihs-base* 380 si::*frs-base* 381 si::*frs-top* 382 si::*tpl-commands* 383 si::*tpl-level* 384 si::frs-top 385 si::ihs-top 386 si::ihs-fun 387 si::ihs-env 388 si::sch-frs-base 389 si::set-break-env 390 si::set-current-ihs 391 si::tpl-commands))) 392 393 (defvar *backtrace* '()) 394 395 (defun in-slynk-package-p (x) 396 (and 397 (symbolp x) 398 (member (symbol-package x) 399 (list #.(find-package :slynk) 400 #.(find-package :slynk-backend) 401 #.(ignore-errors (find-package :slynk-mop)) 402 #.(ignore-errors (find-package :slynk-loader)))) 403 t)) 404 405 (defun is-slynk-source-p (name) 406 (setf name (pathname name)) 407 #+(or) 408 (pathname-match-p 409 name 410 (make-pathname :defaults slynk-loader::*source-directory* 411 :name (pathname-name name) 412 :type (pathname-type name) 413 :version (pathname-version name))) 414 nil) 415 416 (defun is-ignorable-fun-p (x) 417 (or 418 (in-slynk-package-p (frame-name x)) 419 (multiple-value-bind (file position) 420 (ignore-errors (si::compiled-function-file (car x))) 421 (declare (ignore position)) 422 (if file (is-slynk-source-p file))))) 423 424 (defmacro find-ihs-top (x) 425 (declare (ignore x)) 426 '(si::ihs-top)) 427 428 (defimplementation call-with-debugging-environment (debugger-loop-fn) 429 (declare (type function debugger-loop-fn)) 430 (let* (;;(*tpl-commands* si::tpl-commands) 431 (*ihs-base* 0) 432 (*ihs-top* (find-ihs-top 'call-with-debugging-environment)) 433 (*ihs-current* *ihs-top*) 434 (*frs-base* (or (sch-frs-base 0 #|*frs-top*|# *ihs-base*) (1+ (frs-top)))) 435 (*frs-top* (frs-top)) 436 (*read-suppress* nil) 437 ;;(*tpl-level* (1+ *tpl-level*)) 438 (*backtrace* (loop for ihs from 0 below *ihs-top* 439 collect (list (si::ihs-fun ihs) 440 (si::ihs-env ihs) 441 nil)))) 442 (declare (special *ihs-current*)) 443 (loop for f from *frs-base* to *frs-top* 444 do (let ((i (- (si::frs-ihs f) *ihs-base* 1))) 445 (when (plusp i) 446 (let* ((x (elt *backtrace* i)) 447 (name (si::frs-tag f))) 448 (unless (mkcl:fixnump name) 449 (push name (third x))))))) 450 (setf *backtrace* (remove-if #'is-ignorable-fun-p (nreverse *backtrace*))) 451 (setf *tmp* *backtrace*) 452 (set-break-env) 453 (set-current-ihs) 454 (let ((*ihs-base* *ihs-top*)) 455 (funcall debugger-loop-fn)))) 456 457 (defimplementation call-with-debugger-hook (hook fun) 458 (let ((*debugger-hook* hook) 459 (*ihs-base* (find-ihs-top 'call-with-debugger-hook))) 460 (funcall fun))) 461 462 (defimplementation compute-backtrace (start end) 463 (when (numberp end) 464 (setf end (min end (length *backtrace*)))) 465 (loop for f in (subseq *backtrace* start end) 466 collect f)) 467 468 (defimplementation format-sldb-condition (condition) 469 "Format a condition for display in SLDB." 470 ;;(princ-to-string condition) 471 (format nil "~A~%In thread: ~S" condition mt:*thread*) 472 ) 473 474 (defun frame-name (frame) 475 (let ((x (first frame))) 476 (if (symbolp x) 477 x 478 (function-name x)))) 479 480 (defun function-position (fun) 481 (multiple-value-bind (file position) 482 (si::compiled-function-file fun) 483 (and file (make-location 484 `(:file ,(if (stringp file) file (namestring file))) 485 ;;`(:position ,position) 486 `(:end-position , position))))) 487 488 (defun frame-function (frame) 489 (let* ((x (first frame)) 490 fun position) 491 (etypecase x 492 (symbol (and (fboundp x) 493 (setf fun (fdefinition x) 494 position (function-position fun)))) 495 (function (setf fun x position (function-position x)))) 496 (values fun position))) 497 498 (defun frame-decode-env (frame) 499 (let ((functions '()) 500 (blocks '()) 501 (variables '())) 502 (setf frame (si::decode-ihs-env (second frame))) 503 (dolist (record frame) 504 (let* ((record0 (car record)) 505 (record1 (cdr record))) 506 (cond ((or (symbolp record0) (stringp record0)) 507 (setq variables (acons record0 record1 variables))) 508 ((not (mkcl:fixnump record0)) 509 (push record1 functions)) 510 ((symbolp record1) 511 (push record1 blocks)) 512 (t 513 )))) 514 (values functions blocks variables))) 515 516 (defimplementation print-frame (frame stream) 517 (let ((function (first frame))) 518 (let ((fname 519 ;;; (cond ((symbolp function) function) 520 ;;; ((si:instancep function) (slot-value function 'name)) 521 ;;; ((compiled-function-p function) 522 ;;; (or (si::compiled-function-name function) 'lambda)) 523 ;;; (t :zombi)) 524 (si::get-fname function) 525 )) 526 (if (eq fname 'si::bytecode) 527 (format stream "~A [Evaluation of: ~S]" 528 fname (function-lambda-expression function)) 529 (format stream "~A" fname) 530 ) 531 (when (si::closurep function) 532 (format stream 533 ", closure generated from ~A" 534 (si::get-fname (si:closure-producer function))) 535 ) 536 ) 537 ) 538 ) 539 540 (defimplementation frame-source-location (frame-number) 541 (nth-value 1 (frame-function (elt *backtrace* frame-number)))) 542 543 (defimplementation frame-catch-tags (frame-number) 544 (third (elt *backtrace* frame-number))) 545 546 (defimplementation frame-locals (frame-number) 547 (loop for (name . value) in (nth-value 2 (frame-decode-env (elt *backtrace* frame-number))) 548 with i = 0 549 collect (list :name name :id (prog1 i (incf i)) :value value))) 550 551 (defimplementation frame-var-value (frame-number var-id) 552 (cdr (elt (nth-value 2 (frame-decode-env (elt *backtrace* frame-number))) var-id))) 553 554 (defimplementation disassemble-frame (frame-number) 555 (let ((fun (frame-fun (elt *backtrace* frame-number)))) 556 (disassemble fun))) 557 558 (defimplementation eval-in-frame (form frame-number) 559 (let ((env (second (elt *backtrace* frame-number)))) 560 (si:eval-in-env form env))) 561 562 #| 563 (defimplementation gdb-initial-commands () 564 ;; These signals are used by the GC. 565 #+linux '("handle SIGPWR noprint nostop" 566 "handle SIGXCPU noprint nostop")) 567 568 (defimplementation command-line-args () 569 (loop for n from 0 below (si:argc) collect (si:argv n))) 570 |# 571 572 ;;;; Inspector 573 574 (defmethod emacs-inspect ((o t)) 575 ; ecl clos support leaves some to be desired 576 (cond 577 ((streamp o) 578 (list* 579 (format nil "~S is an ordinary stream~%" o) 580 (append 581 (list 582 "Open for " 583 (cond 584 ((ignore-errors (interactive-stream-p o)) "Interactive") 585 ((and (input-stream-p o) (output-stream-p o)) "Input and output") 586 ((input-stream-p o) "Input") 587 ((output-stream-p o) "Output")) 588 `(:newline) `(:newline)) 589 (label-value-line* 590 ("Element type" (stream-element-type o)) 591 ("External format" (stream-external-format o))) 592 (ignore-errors (label-value-line* 593 ("Broadcast streams" (broadcast-stream-streams o)))) 594 (ignore-errors (label-value-line* 595 ("Concatenated streams" (concatenated-stream-streams o)))) 596 (ignore-errors (label-value-line* 597 ("Echo input stream" (echo-stream-input-stream o)))) 598 (ignore-errors (label-value-line* 599 ("Echo output stream" (echo-stream-output-stream o)))) 600 (ignore-errors (label-value-line* 601 ("Output String" (get-output-stream-string o)))) 602 (ignore-errors (label-value-line* 603 ("Synonym symbol" (synonym-stream-symbol o)))) 604 (ignore-errors (label-value-line* 605 ("Input stream" (two-way-stream-input-stream o)))) 606 (ignore-errors (label-value-line* 607 ("Output stream" (two-way-stream-output-stream o))))))) 608 ((si:instancep o) ;;t 609 (let* ((cl (si:instance-class o)) 610 (slots (clos::class-slots cl))) 611 (list* (format nil "~S is an instance of class ~A~%" 612 o (clos::class-name cl)) 613 (loop for x in slots append 614 (let* ((name (clos::slot-definition-name x)) 615 (value (if (slot-boundp o name) 616 (clos::slot-value o name) 617 "Unbound" 618 ))) 619 (list 620 (format nil "~S: " name) 621 `(:value ,value) 622 `(:newline))))))) 623 (t (list (format nil "~A" o))))) 624 625 ;;;; Definitions 626 627 (defimplementation find-definitions (name) 628 (if (fboundp name) 629 (let ((tmp (find-source-location (symbol-function name)))) 630 `(((defun ,name) ,tmp))))) 631 632 (defimplementation find-source-location (obj) 633 (setf *tmp* obj) 634 (or 635 (typecase obj 636 (function 637 (multiple-value-bind (file pos) (ignore-errors (si::compiled-function-file obj)) 638 (if (and file pos) 639 (make-location 640 `(:file ,(if (stringp file) file (namestring file))) 641 `(:end-position ,pos) ;; `(:position ,pos) 642 `(:snippet 643 ,(with-open-file (s file) 644 (file-position s pos) 645 (skip-comments-and-whitespace s) 646 (read-snippet s)))))))) 647 `(:error (format nil "Source definition of ~S not found" obj)))) 648 649 ;;;; Profiling 650 651 652 (eval-when (:compile-toplevel :load-toplevel) 653 ;; At compile-time we need access to the profile package for the 654 ;; the following code to be read properly. 655 ;; It is a bit a shame we have to load the entire module to get that. 656 (require 'profile)) 657 658 659 (defimplementation profile (fname) 660 (when fname (eval `(profile:profile ,fname)))) 661 662 (defimplementation unprofile (fname) 663 (when fname (eval `(profile:unprofile ,fname)))) 664 665 (defimplementation unprofile-all () 666 (profile:unprofile-all) 667 "All functions unprofiled.") 668 669 (defimplementation profile-report () 670 (profile:report)) 671 672 (defimplementation profile-reset () 673 (profile:reset) 674 "Reset profiling counters.") 675 676 (defimplementation profiled-functions () 677 (profile:profile)) 678 679 (defimplementation profile-package (package callers methods) 680 (declare (ignore callers methods)) 681 (eval `(profile:profile ,(package-name (find-package package))))) 682 683 684 ;;;; Threads 685 686 (defvar *thread-id-counter* 0) 687 688 (defvar *thread-id-counter-lock* 689 (mt:make-lock :name "thread id counter lock")) 690 691 (defun next-thread-id () 692 (mt:with-lock (*thread-id-counter-lock*) 693 (incf *thread-id-counter*)) 694 ) 695 696 (defparameter *thread-id-map* (make-hash-table)) 697 (defparameter *id-thread-map* (make-hash-table)) 698 699 (defvar *thread-id-map-lock* 700 (mt:make-lock :name "thread id map lock")) 701 702 (defparameter +default-thread-local-variables+ 703 '(*macroexpand-hook* 704 *default-pathname-defaults* 705 *readtable* 706 *random-state* 707 *compile-print* 708 *compile-verbose* 709 *load-print* 710 *load-verbose* 711 *print-array* 712 *print-base* 713 *print-case* 714 *print-circle* 715 *print-escape* 716 *print-gensym* 717 *print-length* 718 *print-level* 719 *print-lines* 720 *print-miser-width* 721 *print-pprint-dispatch* 722 *print-pretty* 723 *print-radix* 724 *print-readably* 725 *print-right-margin* 726 *read-base* 727 *read-default-float-format* 728 *read-eval* 729 *read-suppress* 730 )) 731 732 (defun thread-local-default-bindings () 733 (let (local) 734 (dolist (var +default-thread-local-variables+ local) 735 (setq local (acons var (symbol-value var) local)) 736 ))) 737 738 ;; mkcl doesn't have weak pointers 739 (defimplementation spawn (fn &key name initial-bindings) 740 (let* ((local-defaults (thread-local-default-bindings)) 741 (thread 742 ;;(mt:make-thread :name name) 743 (mt:make-thread :name name 744 :initial-bindings (nconc initial-bindings 745 local-defaults)) 746 ) 747 (id (next-thread-id))) 748 (mt:with-lock (*thread-id-map-lock*) 749 (setf (gethash id *thread-id-map*) thread) 750 (setf (gethash thread *id-thread-map*) id)) 751 (mt:thread-preset 752 thread 753 #'(lambda () 754 (unwind-protect 755 (progn 756 ;;(format t "~&Starting thread: ~S.~%" name) (finish-output) 757 (mt:thread-detach nil) 758 (funcall fn)) 759 (progn 760 ;;(format t "~&Wrapping up thread: ~S.~%" name) (finish-output) 761 (mt:with-lock (*thread-id-map-lock*) 762 (remhash thread *id-thread-map*) 763 (remhash id *thread-id-map*)) 764 ;;(format t "~&Finished thread: ~S~%" name) (finish-output) 765 )))) 766 (mt:thread-enable thread) 767 (mt:thread-yield) 768 thread 769 )) 770 771 (defimplementation thread-id (thread) 772 (block thread-id 773 (mt:with-lock (*thread-id-map-lock*) 774 (or (gethash thread *id-thread-map*) 775 (let ((id (next-thread-id))) 776 (setf (gethash id *thread-id-map*) thread) 777 (setf (gethash thread *id-thread-map*) id) 778 id))))) 779 780 (defimplementation find-thread (id) 781 (mt:with-lock (*thread-id-map-lock*) 782 (gethash id *thread-id-map*))) 783 784 (defimplementation thread-name (thread) 785 (mt:thread-name thread)) 786 787 (defimplementation thread-status (thread) 788 (if (mt:thread-active-p thread) 789 "RUNNING" 790 "STOPPED")) 791 792 (defimplementation make-lock (&key name) 793 (mt:make-lock :name name :recursive t)) 794 795 (defimplementation call-with-lock-held (lock function) 796 (declare (type function function)) 797 (mt:with-lock (lock) (funcall function))) 798 799 (defimplementation current-thread () 800 mt:*thread*) 801 802 (defimplementation all-threads () 803 (mt:all-threads)) 804 805 (defimplementation interrupt-thread (thread fn) 806 (mt:interrupt-thread thread fn)) 807 808 (defimplementation kill-thread (thread) 809 (mt:interrupt-thread thread #'mt:terminate-thread) 810 ) 811 812 (defimplementation thread-alive-p (thread) 813 (mt:thread-active-p thread)) 814 815 (defvar *mailbox-lock* (mt:make-lock :name "mailbox lock")) 816 (defvar *mailboxes* (list)) 817 (declaim (type list *mailboxes*)) 818 819 (defstruct (mailbox (:conc-name mailbox.)) 820 thread 821 locked-by 822 (mutex (mt:make-lock :name "thread mailbox")) 823 (semaphore (mt:make-semaphore)) 824 (queue '() :type list)) 825 826 (defun mailbox (thread) 827 "Return THREAD's mailbox." 828 (mt:with-lock (*mailbox-lock*) 829 (or (find thread *mailboxes* :key #'mailbox.thread) 830 (let ((mb (make-mailbox :thread thread))) 831 (push mb *mailboxes*) 832 mb)))) 833 834 (defimplementation send (thread message) 835 (handler-case 836 (let* ((mbox (mailbox thread)) 837 (mutex (mailbox.mutex mbox))) 838 ;; (mt:interrupt-thread 839 ;; thread 840 ;; (lambda () 841 ;; (mt:with-lock (mutex) 842 ;; (setf (mailbox.queue mbox) 843 ;; (nconc (mailbox.queue mbox) (list message)))))) 844 845 ;; (format t "~&! thread = ~S~% thread = ~S~% message = ~S~%" 846 ;; mt:*thread* thread message) (finish-output) 847 (mt:with-lock (mutex) 848 (setf (mailbox.locked-by mbox) mt:*thread*) 849 (setf (mailbox.queue mbox) 850 (nconc (mailbox.queue mbox) (list message))) 851 ;;(format t "*") (finish-output) 852 (handler-case 853 (mt:semaphore-signal (mailbox.semaphore mbox)) 854 (condition (condition) 855 (format t "Something went bad with semaphore-signal ~A" condition) (finish-output) 856 ;;(break) 857 )) 858 (setf (mailbox.locked-by mbox) nil) 859 ) 860 ;;(format t "+") (finish-output) 861 ) 862 (condition (condition) 863 (format t "~&Error in send: ~S~%" condition) (finish-output)) 864 ) 865 ) 866 867 ;; (defimplementation receive () 868 ;; (block got-mail 869 ;; (let* ((mbox (mailbox mt:*thread*)) 870 ;; (mutex (mailbox.mutex mbox))) 871 ;; (loop 872 ;; (mt:with-lock (mutex) 873 ;; (if (mailbox.queue mbox) 874 ;; (return-from got-mail (pop (mailbox.queue mbox))))) 875 ;; ;;interrupt-thread will halt this if it takes longer than 1sec 876 ;; (sleep 1))))) 877 878 879 (defimplementation receive-if (test &optional timeout) 880 (handler-case 881 (let* ((mbox (mailbox (current-thread))) 882 (mutex (mailbox.mutex mbox)) 883 got-one) 884 (assert (or (not timeout) (eq timeout t))) 885 (loop 886 (check-slime-interrupts) 887 ;;(format t "~&: ~S~%" mt:*thread*) (finish-output) 888 (handler-case 889 (setq got-one (mt:semaphore-wait (mailbox.semaphore mbox) 2)) 890 (condition (condition) 891 (format t "~&In (slynk-mkcl) receive-if: Something went bad with semaphore-wait ~A~%" condition) 892 (finish-output) 893 nil 894 ) 895 ) 896 (mt:with-lock (mutex) 897 (setf (mailbox.locked-by mbox) mt:*thread*) 898 (let* ((q (mailbox.queue mbox)) 899 (tail (member-if test q))) 900 (when tail 901 (setf (mailbox.queue mbox) (nconc (ldiff q tail) (cdr tail))) 902 (setf (mailbox.locked-by mbox) nil) 903 ;;(format t "~&thread ~S received: ~S~%" mt:*thread* (car tail)) 904 (return (car tail)))) 905 (setf (mailbox.locked-by mbox) nil) 906 ) 907 908 ;;(format t "/ ~S~%" mt:*thread*) (finish-output) 909 (when (eq timeout t) (return (values nil t))) 910 ;; (unless got-one 911 ;; (format t "~&In (slynk-mkcl) receive-if: semaphore-wait timed out!~%")) 912 ) 913 ) 914 (condition (condition) 915 (format t "~&Error in (slynk-mkcl) receive-if: ~S, ~A~%" condition condition) (finish-output) 916 nil 917 ) 918 ) 919 ) 920 921 922 (defmethod stream-finish-output ((stream stream)) 923 (finish-output stream)) 924 925 926 ;; 927 928 ;;#+windows 929 (defimplementation doze-in-repl () 930 (setq *inferior-lisp-sleeping-post* (mt:make-semaphore)) 931 ;;(loop (sleep 1)) 932 (mt:semaphore-wait *inferior-lisp-sleeping-post*) 933 (mk-ext:quit :verbose t) 934 ) 935