diff --git a/README b/README index 816a74c182ed9a3275df8529bcd8c842a44c3f04..f11584a29b887440e5bb8f498d87421c8efb2e2a 100644 --- a/README +++ b/README @@ -1,24 +1,74 @@ -The issue that single-threaded-ccl addresses is as follows. +SINGLE-THREADED-CCL +=================== + +A simple patch to allow Clozure CL (CCL) to run in single-threaded mode. + +This is free software, available under the same license as CCL, LLGPL 2.1. + + +Using It +-------- + +To create a single-threaded ccl, you may:: + + ${CCL_DEFAULT_DIRECTORY}/lx86cl64 --load make-single-threaded-ccl + + +To test that indeed you can run code single-threaded mode, you may:: + + ./single-threaded-ccl --eval '(progn (format t "~S" ccl::*application*) (ccl::show-processes) (ccl:quit))' + +It your CCL is successfully single-threaded, only one process will show. + + +Note that to be able to use many of the dynamically loaded features of CCL, +you will need to either put your single-threaded-ccl in the same directory +as the rest of CCL and use it from there, or you will need to:: + + export CCL_DEFAULT_DIRECTORY=/path/to/original-ccl + + +If you build with ASDF, you may also depend on the system single-threaded-ccl, +and when you next save an image, it will start single-threaded. + + +Last Tested +----------- + +It was last tested to run with this checkout of CCL, revision 15393. + http://svn.clozure.com/publicsvn/openmcl/trunk/linuxx86/ccl + + +Won't Make It Upstream +---------------------- + +This was submitted for inclusion in the upstream CCL distribution. +However, Gary Byers has expressed his disinterest in offering and maintaining +an option for single-threaded startup in the upstream CCL, +even when I proposed that I could do the porting and maintaining myself. + + + +Why We Need It +-------------- CCL by default starts ancillary threads early during initialization, -which makes any subsequent attempt to use fork (without a prompt exec -as in run-program) almost guaranteed to lead to massive instability. +which makes any subsequent attempt to use fork +almost guaranteed to lead to massive instability +(unless maybe it's promptly followed by exec, as in run-program, +without a GC interrupt in between). See my blog post: http://fare.livejournal.com/148185.html -However, I have been maintaining this "single-threaded CCL" -modification that can create an image of CCL that doesn't start these -ancillary threads (at the cost of having to manually flush output -streams and of not being able to interrupt computations interactively -with Ctrl-C). - -I use this single-threaded CCL for parallel build with POIU, and have -had to update it once in a while as upstream upgrades sometimes break -it. I also intend to use it in other situations where fork-based -concurrency is called fork: pre-forking servers, robust erlang-style -concurrency, isolating semi-trusted C libraries, etc. - -For the record, Gary Byers has expressed his disinterest in offering -and maintaining an option for single-threaded startup in the upstream -CCL, even when I proposed that I could do the porting and maintaining -myself. +However, I have been maintaining this "single-threaded CCL" modification +that can create an image of CCL that doesn't start these ancillary threads +(at the cost of having to manually flush output streams and +of not being able to interrupt computations interactively with Ctrl-C). + +I use this single-threaded CCL for parallel build with POIU and XCVB, +and have had to update it once in a while +as upstream upgrades sometimes break it. +I also intend to use it in other situations +where fork-based concurrency is called for: +pre-forking servers, robust erlang-style concurrency, +isolating semi-trusted C libraries, etc. diff --git a/build.xcvb b/build.xcvb new file mode 100644 index 0000000000000000000000000000000000000000..ac8f032c34b7a99c4d3d7e2b5a1ab5fad9a9291d --- /dev/null +++ b/build.xcvb @@ -0,0 +1,4 @@ +(module + (:fullname "single-threaded-ccl" + :depends-on ((:when (:featurep :clozure) "single-threaded-ccl")) + :supersedes-asdf ("single-threaded-ccl"))) diff --git a/make-single-threaded-ccl.lisp b/make-single-threaded-ccl.lisp index f1c192d87b37fa684eaa8ec6634f1e6701c1deb1..f53f279155e19c7157cff6c8356dc6b6f2b0a46b 100644 --- a/make-single-threaded-ccl.lisp +++ b/make-single-threaded-ccl.lisp @@ -1,86 +1,10 @@ -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;;; ;;; -;;; Free Software under the GNU LLGPL 2.1 (same as CCL itself) ;;; -;;; ;;; -;;; Copyright (c) 2008-2011 ITA Software, Inc. All rights reserved. ;;; -;;; ;;; -;;; Original authors: Francois-Rene Rideau ;;; -;;; ;;; -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -#| -This is a simple patch to allow Clozure CL (CCL) to run in single-threaded mode. - -It was last tested to run with this checkout of CCL, revision 13568 (same as 13679). - http://svn.clozure.com/publicsvn/openmcl/branches/qres - -This is free software, available under the same license as CCL. - -Note: This was submitted for inclusion in the upstream CCL distribution. -However, Gary Byers is unwilling to include and maintain this patch. - -To create a single-threaded ccl, you may: - ${CCL_DEFAULT_DIRECTORY}/lx86cl64 --load make-single-threaded-ccl -To test that indeed you can run code single-threaded mode, you may: - ./single-threaded-ccl --eval '(progn (format t "~S" ccl::*application*) (ccl::show-processes) (ccl:quit))' - -Note that to be able to use many of the dynamically loaded features of CCL, -you will need to either put your single-threaded-ccl in the same directory -as the rest of CCL and use it from there, or you will need to - export CCL_DEFAULT_DIRECTORY=/path/to/ccl -|# - -(in-package :ccl) - -(defun finish-outputs () - (finish-output *standard-output*) - (finish-output *error-output*) - (finish-output *terminal-output*) - (finish-output *trace-output*) - (housekeeping)) - -(defun show-processes () - (let ((p (all-processes))) - (format t "~&Total number of running lisp processes: ~d~%~W~%" (length p) p) - (finish-outputs))) - -(defun flushing-rep () - (print-listener-prompt *standard-output*) - (finish-outputs) - (let* ((eof '#.'#:eof) - (sexp (read *standard-input* nil eof nil))) - (when (eq sexp eof) - (finish-outputs) - (throw :flushing-rep-eof nil)) - ;; This use of eval is OK because it's part of the build infrastructure. - (format t "~&~S~%" (eval sexp)) - (finish-outputs))) - -(defun flushing-repl () - (catch :flushing-rep-eof - (loop (flushing-rep)))) - -(defun single-threaded-toplevel () - (housekeeping) - ;;(show-processes) - ;;(flushing-repl) - (listener-function) - (quit 0)) - -(defclass single-threaded-lisp-development-system (lisp-development-system) - ()) - -(defmethod repl-function-name ((a single-threaded-lisp-development-system)) - 'single-threaded-toplevel) - -(defmethod toplevel-function ((a single-threaded-lisp-development-system) init-file) - (%set-toplevel (or (repl-function-name a) 'single-threaded-toplevel)) - (startup-ccl (and *load-lisp-init-file* init-file)) - (toplevel)) - -(defparameter *application* (make-instance 'single-threaded-lisp-development-system)) +":" ; exec ccl --load "$@" ; exit 42 (in-package :cl-user) -;;(save-application "single-threaded-ccl" :toplevel-function #'single-threaded-toplevel :prepend-kernel t) +(load (merge-pathnames "single-threaded-ccl" *load-truename*)) -(save-application "single-threaded-ccl" :prepend-kernel t) +(ccl:save-application + "single-threaded-ccl" + ;; :toplevel-function #'ccl::single-threaded-toplevel + :prepend-kernel t) diff --git a/single-threaded-ccl.asd b/single-threaded-ccl.asd new file mode 100644 index 0000000000000000000000000000000000000000..9a8357d1889625b9f3356368644cd4ee5905a3df --- /dev/null +++ b/single-threaded-ccl.asd @@ -0,0 +1,14 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;; ;;; +;;; Free Software under the GNU LLGPL 2.1 (same as CCL itself) ;;; +;;; ;;; +;;; Copyright (c) 2008-2011 ITA Software, Inc. All rights reserved. ;;; +;;; Copyright (c) 2011-2012 Google, Inc. All rights reserved. ;;; +;;; ;;; +;;; Original authors: Francois-Rene Rideau ;;; +;;; ;;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(defsystem single-threaded-ccl + :license "LLGPL 2.1" + :components (#+clozure (:file "single-threaded-ccl"))) diff --git a/single-threaded-ccl.lisp b/single-threaded-ccl.lisp new file mode 100644 index 0000000000000000000000000000000000000000..4eb061430834da7d5bf5389d6dbb26d57940909e --- /dev/null +++ b/single-threaded-ccl.lisp @@ -0,0 +1,59 @@ +#+xcvb (module ()) + +(in-package :ccl) + +(defun finish-outputs () + (finish-output *standard-output*) + (finish-output *error-output*) + (finish-output *terminal-output*) + (finish-output *trace-output*) + (housekeeping)) + +(defun show-processes () + (let ((p (all-processes))) + (format t "~&Total number of running lisp processes: ~d~%~W~%" (length p) p) + (finish-outputs))) + +(defun flushing-rep () + (print-listener-prompt *standard-output*) + (finish-outputs) + (let* ((eof '#.'#:eof) + (sexp (read *standard-input* nil eof nil))) + (when (eq sexp eof) + (finish-outputs) + (throw :flushing-rep-eof nil)) + ;; This use of eval is OK because it's part of the build infrastructure. + (format t "~&~S~%" (eval sexp)) + (finish-outputs))) + +(defun flushing-repl () + (catch :flushing-rep-eof + (loop (flushing-rep)))) + +(defun single-threaded-toplevel () + (housekeeping) + ;;(show-processes) + ;;(flushing-repl) + (listener-function) + (quit 0)) + +(defclass single-threaded-lisp-development-system (lisp-development-system) + ()) + +(defmethod repl-function-name ((a single-threaded-lisp-development-system)) + 'single-threaded-toplevel) + +(defmethod toplevel-function ((a single-threaded-lisp-development-system) init-file) + (%set-toplevel (or (repl-function-name a) 'single-threaded-toplevel)) + (startup-ccl (and *load-lisp-init-file* init-file)) + (toplevel)) + +(defparameter *application* (make-instance 'single-threaded-lisp-development-system)) + +#| +;; Use it as follows: +(ccl::save-application + "single-threaded-ccl" + ;; :toplevel-function #'single-threaded-toplevel + :prepend-kernel t) +|#