X-Git-Url: http://repo.macrolet.net/gitweb/?a=blobdiff_plain;f=src%2Fcode%2Ftoplevel.lisp;h=05445b9f36b481b74e46635dcbcc654c849648b4;hb=cc27e35fc73e6765679d6f426ee144abdfac7c27;hp=87a47526f89c2e2072cacf7f378066c3caa65ce8;hpb=406c4143b9aa508c35c0004f57f587170dfda166;p=sbcl.git diff --git a/src/code/toplevel.lisp b/src/code/toplevel.lisp index 87a4752..05445b9 100644 --- a/src/code/toplevel.lisp +++ b/src/code/toplevel.lisp @@ -56,16 +56,17 @@ (defvar *sysinit-pathname-function* #'sysinit-pathname #!+sb-doc - "Designator for a function of zero arguments called to obtain a pathname -designator for the default sysinit file, or NIL. If the function returns NIL, -no sysinit file is used unless one has been specified on the command-line.") + "Designator for a function of zero arguments called to obtain a +pathname designator for the default sysinit file, or NIL. If the +function returns NIL, no sysinit file is used unless one has been +specified on the command-line.") (defvar *userinit-pathname-function* #'userinit-pathname #!+sb-doc - "Designator for a function of zero arguments called to obtain a pathname -designator or a stream for the default userinit file, or NIL. If the function -returns NIL, no userinit file is used unless one has been specified on the -command-line.") + "Designator for a function of zero arguments called to obtain a +pathname designator or a stream for the default userinit file, or NIL. +If the function returns NIL, no userinit file is used unless one has +been specified on the command-line.") ;;;; miscellaneous utilities for working with with TOPLEVEL @@ -75,16 +76,28 @@ command-line.") ;;; handled appropriately. (defmacro handling-end-of-the-world (&body body) (with-unique-names (caught) - `(let ((,caught (catch '%end-of-the-world - (/show0 "inside CATCH '%END-OF-THE-WORLD") - (unwind-protect - (progn ,@body) - (call-hooks "exit" *exit-hooks*))))) - (/show0 "back from CATCH '%END-OF-THE-WORLD, flushing output") - (flush-standard-output-streams) - (sb!thread::terminate-session) - (/show0 "calling UNIX-EXIT") - (sb!unix:unix-exit ,caught)))) + `(without-interrupts + (let ((,caught + (catch '%end-of-the-world + (unwind-protect + (with-local-interrupts ,@body (quit)) + (handler-case + (with-local-interrupts + (call-hooks "exit" *exit-hooks* :on-error :warn)) + (serious-condition () + 1)))))) + ;; If user called QUIT and exit hooks were OK, the status is what it + ;; is -- even eg. streams cannot be flushed anymore. Even if + ;; something goes wrong now, we still report what was asked. We still + ;; want to have %END-OF-THE-WORLD visible, though. + (catch '%end-of-the-world + (handler-case + (unwind-protect + (progn + (flush-standard-output-streams) + (sb!thread::terminate-session)) + (sb!unix:unix-exit ,caught)) + (serious-condition ()))))))) ;;;; working with *CURRENT-ERROR-DEPTH* and *MAXIMUM-ERROR-DEPTH* @@ -156,126 +169,35 @@ command-line.") ;;;; miscellaneous external functions -(defun sleep (n) +(defun sleep (seconds) #!+sb-doc - "This function causes execution to be suspended for N seconds. N may - be any non-negative, non-complex number." - (when (or (not (realp n)) - (minusp n)) + "This function causes execution to be suspended for SECONDS. SECONDS may be +any non-negative real number." + (when (or (not (realp seconds)) + (minusp seconds)) (error 'simple-type-error :format-control "invalid argument to SLEEP: ~S" - :format-arguments (list n) - :datum n + :format-arguments (list seconds) + :datum seconds :expected-type '(real 0))) #!-win32 (multiple-value-bind (sec nsec) - (if (integerp n) - (values n 0) + (if (integerp seconds) + (values seconds 0) (multiple-value-bind (sec frac) - (truncate n) + (truncate seconds) (values sec (truncate frac 1e-9)))) + ;; nanosleep() accepts time_t as the first argument, but on some platforms + ;; it is restricted to 100 million seconds. Maybe someone can actually + ;; have a reason to sleep for over 3 years? + (loop while (> sec (expt 10 8)) + do (decf sec (expt 10 8)) + (sb!unix:nanosleep (expt 10 8) 0)) (sb!unix:nanosleep sec nsec)) #!+win32 - (sb!win32:millisleep (truncate (* n 1000))) + (sb!win32:millisleep (truncate (* seconds 1000))) nil) -;;;; SCRUB-CONTROL-STACK - -(defconstant bytes-per-scrub-unit 2048) - -;;; Zero the unused portion of the control stack so that old objects -;;; are not kept alive because of uninitialized stack variables. - -;;; "To summarize the problem, since not all allocated stack frame -;;; slots are guaranteed to be written by the time you call an another -;;; function or GC, there may be garbage pointers retained in your -;;; dead stack locations. The stack scrubbing only affects the part -;;; of the stack from the SP to the end of the allocated stack." -;;; - ram, on cmucl-imp, Tue, 25 Sep 2001 - -;;; So, as an (admittedly lame) workaround, from time to time we call -;;; scrub-control-stack to zero out all the unused portion. This is -;;; supposed to happen when the stack is mostly empty, so that we have -;;; a chance of clearing more of it: callers are currently (2002.07.18) -;;; REPL and SUB-GC - -(defun scrub-control-stack () - (declare (optimize (speed 3) (safety 0)) - (values (unsigned-byte 20))) ; FIXME: DECLARE VALUES? - - #!-stack-grows-downward-not-upward - (let* ((csp (sap-int (sb!c::control-stack-pointer-sap))) - (initial-offset (logand csp (1- bytes-per-scrub-unit))) - (end-of-stack - (- (sap-int (sb!di::descriptor-sap sb!vm:*control-stack-end*)) - (* 2 sb!c:*backend-page-bytes*)))) - (labels - ((scrub (ptr offset count) - (declare (type system-area-pointer ptr) - (type (unsigned-byte 16) offset) - (type (unsigned-byte 20) count) - (values (unsigned-byte 20))) - (cond ((>= (sap-int ptr) end-of-stack) 0) - ((= offset bytes-per-scrub-unit) - (look (sap+ ptr bytes-per-scrub-unit) 0 count)) - (t - (setf (sap-ref-word ptr offset) 0) - (scrub ptr (+ offset sb!vm:n-word-bytes) count)))) - (look (ptr offset count) - (declare (type system-area-pointer ptr) - (type (unsigned-byte 16) offset) - (type (unsigned-byte 20) count) - (values (unsigned-byte 20))) - (cond ((>= (sap-int ptr) end-of-stack) 0) - ((= offset bytes-per-scrub-unit) - count) - ((zerop (sap-ref-word ptr offset)) - (look ptr (+ offset sb!vm:n-word-bytes) count)) - (t - (scrub ptr offset (+ count sb!vm:n-word-bytes)))))) - (declare (type sb!vm::word csp)) - (scrub (int-sap (- csp initial-offset)) - (* (floor initial-offset sb!vm:n-word-bytes) sb!vm:n-word-bytes) - 0))) - - #!+stack-grows-downward-not-upward - (let* ((csp (sap-int (sb!c::control-stack-pointer-sap))) - (end-of-stack (+ (sap-int - (sb!di::descriptor-sap sb!vm:*control-stack-start*)) - (* 2 sb!c:*backend-page-bytes*))) - (initial-offset (logand csp (1- bytes-per-scrub-unit)))) - (labels - ((scrub (ptr offset count) - (declare (type system-area-pointer ptr) - (type (unsigned-byte 16) offset) - (type (unsigned-byte 20) count) - (values (unsigned-byte 20))) - (let ((loc (int-sap (- (sap-int ptr) (+ offset sb!vm:n-word-bytes))))) - (cond ((< (sap-int loc) end-of-stack) 0) - ((= offset bytes-per-scrub-unit) - (look (int-sap (- (sap-int ptr) bytes-per-scrub-unit)) - 0 count)) - (t ;; need to fix bug in %SET-STACK-REF - (setf (sap-ref-word loc 0) 0) - (scrub ptr (+ offset sb!vm:n-word-bytes) count))))) - (look (ptr offset count) - (declare (type system-area-pointer ptr) - (type (unsigned-byte 16) offset) - (type (unsigned-byte 20) count) - (values (unsigned-byte 20))) - (let ((loc (int-sap (- (sap-int ptr) offset)))) - (cond ((< (sap-int loc) end-of-stack) 0) - ((= offset bytes-per-scrub-unit) - count) - ((zerop (sb!kernel::get-lisp-obj-address (stack-ref loc 0))) - (look ptr (+ offset sb!vm:n-word-bytes) count)) - (t - (scrub ptr offset (+ count sb!vm:n-word-bytes))))))) - (declare (type sb!vm::word csp)) - (scrub (int-sap (+ csp initial-offset)) - (* (floor initial-offset sb!vm:n-word-bytes) sb!vm:n-word-bytes) - 0)))) - ;;;; the default toplevel function (defvar / nil @@ -336,30 +258,19 @@ command-line.") (values "sysinit" *sysinit-pathname-function*)) (:user (values "userinit" *userinit-pathname-function*))) - (flet ((process-stream (stream pathname) - (with-simple-restart (abort "Skip rest of ~A file ~S." - context (native-namestring pathname)) - (loop - (with-simple-restart - (continue "Ignore error and continue processing ~A file ~S." - context (native-namestring pathname)) - (let ((form (read stream nil stream))) - (if (eq stream form) - (return-from process-init-file nil) - (eval form)))))))) - (if specified-pathname - (with-open-file (stream (parse-native-namestring specified-pathname) - :if-does-not-exist nil) - (if stream - (process-stream stream (pathname stream)) - (cerror "Ignore missing init file" - "The specified ~A file ~A was not found." - context specified-pathname))) - (let ((default (funcall default-function))) - (when default - (with-open-file (stream (pathname default) :if-does-not-exist nil) - (when stream - (process-stream stream (pathname stream)))))))))) + (if specified-pathname + (with-open-file (stream (parse-native-namestring specified-pathname) + :if-does-not-exist nil) + (if stream + (load-as-source stream :context context) + (cerror "Ignore missing init file" + "The specified ~A file ~A was not found." + context specified-pathname))) + (let ((default (funcall default-function))) + (when default + (with-open-file (stream (pathname default) :if-does-not-exist nil) + (when stream + (load-as-source stream :context context)))))))) (defun process-eval/load-options (options) (/show0 "handling --eval and --load options") @@ -383,27 +294,31 @@ command-line.") (dolist (option options) (process-1 option))))) -;;; Skips past the shebang line on stream, if any. -(defun maybe-skip-shebang-line (stream) - (let ((p (file-position stream))) - (flet ((next () (read-byte stream nil))) - (unwind-protect - (when (and (eq (next) (char-code #\#)) - (eq (next) (char-code #\!))) - (setf p nil) - (loop for x = (next) - until (or (not x) (eq x (char-code #\newline))))) - (when p - (file-position stream p)))) - t)) - (defun process-script (script) - (let ((pathname (native-pathname script))) + (flet ((load-script (stream) + ;; Scripts don't need to be stylish or fast, but silence is usually a + ;; desirable quality... + (handler-bind (((or style-warning compiler-note) #'muffle-warning) + (stream-error (lambda (e) + ;; Shell-style. + (when (member (stream-error-stream e) + (list *stdout* *stdin* *stderr*)) + (quit))))) + ;; Let's not use the *TTY* for scripts, ok? Also, normally we use + ;; synonym streams, but in order to have the broken pipe/eof error + ;; handling right we want to bind them for scripts. + (let ((*terminal-io* (make-two-way-stream *stdin* *stdout*)) + (*debug-io* (make-two-way-stream *stdin* *stderr*)) + (*standard-input* *stdin*) + (*standard-output* *stdout*) + (*error-output* *stderr*)) + (load stream :verbose nil :print nil))))) (handling-end-of-the-world - (with-open-file (f pathname :element-type :default) - (maybe-skip-shebang-line f) - (load f :verbose nil :print nil) - (quit))))) + (if (eq t script) + (load-script *stdin*) + (with-open-file (f (native-pathname script) :element-type :default) + (sb!fasl::maybe-skip-shebang-line f) + (load-script f)))))) ;; Errors while processing the command line cause the system to QUIT, ;; instead of trying to go into the Lisp debugger, because trying to @@ -467,7 +382,7 @@ command-line.") (setf disable-debugger t no-userinit t no-sysinit t - script (pop-option)) + script (if options (pop-option) t)) (return)) ((string= option "--sysinit") (pop-option) @@ -623,7 +538,7 @@ that provides the REPL for the system. Assumes that *STANDARD-INPUT* and ;; should have unwound enough stack by the time we get ;; here that this is now possible. #!-win32 - (sb!kernel::protect-control-stack-guard-page 1) + (sb!kernel::reset-control-stack-guard-page) (funcall repl-fun noprint) (critically-unreachable "after REPL")))))))))