;;; ;;; SIMPLE AND SAFE SLIME MODE ;;; ========================== ;;; ;;; Loading this file before calling any EQL function (involving the UI) ;;; guarantees running EQL functions on the UI thread. ;;; ;;; This means that we don't need a Slime REPL-hook, making it safe to evaluate ;;; any EQL code in Slime, both on the REPL and using 'eval-region'. ;;; ;;; The only drawback is a little more consing for every EQL function call, but ;;; allowing to safely call UI functions from any ECL thread. ;;; ;;; Note also that wrapping functions in QRUN* (like done here, see below) is ;;; basically the same as a direct call, if called from the ECL main thread (UI ;;; thread), so it will add almost no overhead. Since most EQL function calls ;;; are driven by the Qt event loop anyway, you won't even notice the presence ;;; of macro QRUN* (performance wise). ;;; ;;; N.B: If you want to start/run EQL in a thread (other than the UI one), you ;;; just need to add this file to your project. Note that any EQL function call ;;; will now do a thread switch internally, so you may experience (much) slower ;;; execution. See also comment in 'examples/X-extras/primes-thread.lisp' about ;;; manually wrapping repeated calls in macro QRUN*. ;;; (in-package :eql) (setf *slime-mode* :thread-safe) (defmacro wrap-in-qrun* (names &rest arguments) (let* ((fun (if (atom names) names (first names))) (alias (unless (atom names) (second names))) (orig (intern (format nil "%~A-ORIG%" (string-left-trim "%" (symbol-name fun)))))) `(progn (defvar ,orig (symbol-function ',fun)) ; hold a reference to original (setf (symbol-function ',orig) ,orig) (defun ,fun (,@arguments) ; re-define function (qrun* ,(if arguments `(,orig ,@(remove '&optional (mapcar (lambda (x) (if (atom x) x (first x))) arguments))) `(,orig)))) ,(when alias `(setf (symbol-function ',alias) #',fun))))) (wrap-in-qrun* %qnew-instance name arguments) (wrap-in-qrun* %qinvoke-method object cast function arguments) (wrap-in-qrun* (qproperty qget) object name) (wrap-in-qrun* (qset-property qset) object name value) (wrap-in-qrun* %qconnect caller signal receiver slot) (wrap-in-qrun* %qdisconnect caller signal receiver slot) (wrap-in-qrun* qoverride object name function) (wrap-in-qrun* qadd-event-filter object event function) (wrap-in-qrun* %qapropos search class type no-offset) (wrap-in-qrun* qcall-default) (wrap-in-qrun* qclear-event-filters) (wrap-in-qrun* qcopy object) (wrap-in-qrun* %qdelete object later) (wrap-in-qrun* %qexec milliseconds) (wrap-in-qrun* qexit) (wrap-in-qrun* qgui &optional process-events) (wrap-in-qrun* %qload-c++ library-name unload) (wrap-in-qrun* qload-ui file-name) (wrap-in-qrun* (qmessage-box qmsg) x) (wrap-in-qrun* qprocess-events) (wrap-in-qrun* qproperties object &optional (depth 1)) (wrap-in-qrun* (qquit qq) &optional (exit-status 0) (kill-all-threads t)) (wrap-in-qrun* qremove-event-filter handle) (wrap-in-qrun* %qrequire name quiet) (wrap-in-qrun* (qselect qsel) &optional on-selected) (wrap-in-qrun* qset-null object &optional (test t)) (wrap-in-qrun* %qsingle-shot milliseconds function)