CLIM mail archive

[Prev][Next][Index][Thread]

CLIM & multitasking in Lucid ....




Hi -

This message should be relevant to those of you that are trying to use Lucid
CLIM 1.0 with the multitasking facility.

We use the Lucid multitasking facilities in our CLIM applications - and have
encountered process lock errors when running more than one CLIM process and
moving the mouse between application frames. These errors typically look like:

  >>Error: Attempting to unlock a lock held by #<Process My-Process 19D3DDE>

  XLIB:EVENT-LISTEN:
     Required arg 0 (DISPLAY): #<XLIB:DISPLAY kona:0 (MIT X Consortium R4)>
     Optional arg 1 (TIMEOUT): 0
  :C  0: Continue or smash lock.
  :A  1: Abort to Lisp Top Level

  -> 

We have tracked this error down to a low-level bug in the CLX software running
in Lucid Common Lisp on a Sun 4. From looking at the source code for the CLX
macro, this problem appears to originate in the public domain version of CLX
and is specific to Lucid - the macro is conditionally defined by a compiler
switch. Consequently, the problem may exist in Lucid releases of CLIM for
other platforms - but we are not able to verify this.

The offending CLX macro is a multitasking primitive (it handles the process
locks) and is used pervasively by higher level CLX functions & macros - thus
simply recompiling the macro and relevant functions is impractical.

Through some trial and error - we were able to come up with a patch that
allows us to work around the CLX problem.

In this message, I've included (1) two advised functions that - when loaded
into the Lucid CLIM image - allow multitasking to work as expected, (2) the
original CLX macro and my kludged fix to this macro, and (3) a simple CLIM
multitasking example that demonstrates the problem and the fix. A more elegant
solution is probably possible, but this is what we were able to came up with.

The bug & patch were identified originally in the ``Lucid CLIM Developer's
Prerelease for Sun 4'' and reported to Lucid back in June of this year.

Evidently, the most recent release of CLIM we have (``Lucid CLIM Version 1.0
Beta'') still has this multitasking problem.

We were told by Lucid that using CLIM with multiple processes requires that
``each process must have its own root window''.

With these patches, you should be able to use Lucid CLIM-1.0 with the Lucid
multitasking facility with only one root window.

I recognize that multitasking behavior is not defined as being part of the
CLIM-1.0 environment, so the interaction of CLIM with multitasking is probably
whatever the vendor defines it as being.

I think having to use multiple root windows is a bug.

Perhaps a CLIM guru could clarify this issue?

Any way, I hope these patches help .... or that somebody can come up with a
better solution.

- Bill

(in-package :xlib)

 ;;
;;;; Multitasking Patch:
 ;;

;;;; Running several concurrent CLIM applications generates a process unlock
;;;; error. This is due to a low-level bug in the Lucid specific version of
;;;; the HOLDING-LOCK macro -- which is part of the CLX.R4.4 package (we have
;;;; sources for this) and apparently other CLX versions

(defvar *SUPPRESS-MULTITASKING-WARNING-MESSAGES* t
  "Switch that allows the user to suppress the warning PROCESS-UNLOCK messages.")

(LCL:defadvice (LUCID::PROCESS-UNLOCK-FUNCTION-3-ARG clx-patch)
    (getter setter action lock-value &rest args)
  (lcl:with-scheduling-inhibited
      (let ((value (apply getter args)))
	(if (or (eq value lcl:*current-process*) (eq action :ignore))
	    (LCL:apply-advice-continue getter setter action lock-value args)
	    (progn
	      (unless *SUPPRESS-MULTITASKING-WARNING-MESSAGES*
		(warn "PROCESS-UNLOCK-FUNCTION-3-ARG :: attempt to unlock lock held by process >> ~s" value))
	      (LCL:apply-advice-continue getter setter :ignore lock-value args))))))

(LCL:defadvice (LUCID::PROCESS-UNLOCK-FUNCTION-4-ARG clx-patch)
    (getter setter action lock-value &rest args)
  (lcl:with-scheduling-inhibited
      (let ((value (apply getter args)))
	(if (or (eq value lcl:*current-process*) (eq action :ignore))
	    (LCL:apply-advice-continue getter setter action lock-value args)
	    (progn
	      (unless *SUPPRESS-MULTITASKING-WARNING-MESSAGES*
		(warn "PROCESS-UNLOCK-FUNCTION-4-ARG :: attempt to unlock lock held by process >> ~s" value))
	      (LCL:apply-advice-continue getter setter :ignore lock-value args))))))

;;;; Description of the bug in HOLDING-LOCK macro (see source code below) ....
;;;;
;;;; Condition number 4 - which executes the PROCESS-WAIT-WITH-TIMEOUT may not
;;;; actually have the lock (eg, a timeout occurs and the CONDITIONAL-STORE is
;;;; never executed - thus executing PROCESS-UNLOCK in the UNWIND-PROTECT will
;;;; signal an error.

;; FILE: dependent.lisp

;; Original definition:
#+ignore
;; Lucid has a process locking mechanism as well under release 3.0
#+lcl3.0
(defmacro holding-lock ((locator display &optional whostate &key timeout) &body body)
  (declare (ignore display))
  (if timeout
      ;; Hair to support timeout.
      `(let ((.have-lock. (eq ,locator lcl:*current-process*))
	     (.timeout. ,timeout))
	 (unwind-protect
	     (when (cond (.have-lock.)
			 ((conditional-store ,locator nil lcl:*current-process*))
			 ((null .timeout.)
			  (lcl:process-lock ,locator)
			  t)
			 ((lcl:process-wait-with-timeout ,whostate .timeout.
			    #'(lambda ()
				(conditional-store ,locator nil lcl:*current-process*)))))
	       ,@body)
	   (unless .have-lock. 
	     (lcl:process-unlock ,locator))))
    `(lcl:with-process-lock (,locator)
       ,@body)))

;; My hacked fix:

(defmacro holding-lock ((locator display &optional whostate &key timeout) &body body)
  (declare (ignore display))
  (if timeout
      ;; Hair to support timeout.
      `(let ((.have-lock. (eq ,locator lcl:*current-process*))
	     (.timeout. ,timeout))
	 (unwind-protect
	     (when (cond (.have-lock.)
			 ((conditional-store ,locator nil lcl:*current-process*))
			 ((null .timeout.)
			  (lcl:process-lock ,locator)
			  t)
			 ;; this is kludged to abort the PROCESS-UNLOCK:
			 ((or (lcl:process-wait-with-timeout ,whostate .timeout.
				#'(lambda () (conditional-store ,locator nil lcl:*current-process*)))
			      (progn (setf .have-lock. :abort) nil))))
	       ,@body)
	   (unless .have-lock. 
	     (lcl:process-unlock ,locator))))
    `(lcl:with-process-lock (,locator)
       ,@body)))

;;; The following simple example can be used to illustrate the multitasking
;;; bug. Compile and load the application and start up several processes (via
;;; the I/O-UI function) - a process unlocking error will be signalled inside
;;; of EVENT-LISTEN. Loading the above advise patches will fix the problem in
;;; this example and other cases that we have run across.

(in-package :bug :use '(:clim-lisp :clim))

(define-application-frame I/O-UI ()
  ()
  (:panes ((title   :title :display-string "I/O User Interface")
	   (menu    :command-menu :stream-background +lavender-blush+)
	   (doc     :pointer-documentation :stream-background +lavender-blush+)
	   (i/o     :interactor :stream-background +lavender-blush+)))
  (:layout ((dataflow (:column 1
			       (menu    4/10)
			       (i/o     3/10)
			       (doc     3/10))))))
;; Launching Function:

(defun I/O-UI (&rest args)
  (let ((frame (apply #'make-application-frame 'I/O-UI
		      :parent *root* :width 200 :height 200 args)))
    (user::make-process :name "I/O User Interface"
			:function #'run-frame-top-level :args (list frame))))

;; The way out:

(define-i/o-ui-command (COM-QUIT :name "Quit I/O UI"
				 :menu "Quit")
    ()
  (let ((w (frame-top-level-window *APPLICATION-FRAME*)))
    (setf (window-visibility w) nil)
    (force-output w))
  (frame-exit *APPLICATION-FRAME*))

(define-i/o-ui-command (COM-Y-OR-N-P :name "Yes or No (Y or N)"
				     :menu "Yes or No (Y or N)")
    ()
  (format t "~% user response was --> ~s~%" (y-or-n-p "Will this work")))

(define-i/o-ui-command (COM-READ :name "Read something"
				 :menu "Read something")
    ()
  (format t "~% just read --> ~s~%" (read)))



0,,


Main Index | Thread Index