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

Re: Garbage Collector Troubles



At  6:55 PM 7/20/93 -0400, Michael Burks wrote:
>I would like to know more about exactly how LISP decides to gray out my
>windows and how it goes about it.
>I have been trying to track down this obscure bug for some time.  Here
>is the situation:
>
>I have an auto-offscreen-window class, that uses a view-draw-contents
>:around method to force all drawing to an offscreen gworld, as opposed
>to the screen, the idea being that
>
>A) redrawing the window when the image isn't trashed is one copybits call.
>B) All the drawing appears at once.
>
>The class is written so as to be completely transparent, i.e., you could
>take a finished window class, add auto-offscreen-window to its
>inheritance list, and everything would work the way it should.

I've wanted such a beast for a while, but haven't ever made the time to do it.
Can you make your code public when it's ready?

[...]

>I override all of the drawing methods (i.e., frame-rect, line-to, etc.)
>to accomplish the transparency.  These methods set the port according to
>whether you are drawing offscreen.

Hmm... sounds like it's not "completely transparent", but I'm still interested.

>
>The point -> How is the feature that grays out the windows with pending
>updates implemented?  Right now my feeling is that LISP's operations on
>my window are causing some bad interaction, since the bug NEVER occurs
>at any other time.
>
>Any help would be appreciated, and if the tone of this message is angry,
>I'm sorry, I've been looking for this bug for a long time.

You don't sound overly angry. I was remiss in not answering your
message of June 22 in which you implied that you were having
gc-related crashes, but other than that I can find very little
squawking from you in our mail archive. Now that you've made some
noise, you've got my attention.

First off, as I said to Tom McDermott yesterday, you can completely
disable the garbage collector's event polling, which causes it to
gray out update regions when it gets update events, with
(set-gc-event-check-enabled-p nil). This should get you back on your
feet again, and should give me a little time to figure out what the
problem is.

I would guess that you're crashing inside of MCL's growzone function,
which gets called whenever a request for Mac Heap cannot be
imiediately satisfied. MCL's response is to GC, which will free any
windows that have been closed but not yet #_CloseWindow'd, then, if
there's still not a large enough free block, it moves the border
between the Lisp heap and the Mac heap. It may be some other
interaction of your gworld code and the garbage collector, however.

Here's the code that the garbage collector executes when it receives
an update event:

         if# eq then.s          ; "handle" update events, sort of.
          bset #gc_update_bit,3+vc_gc_event_status_bits(nilreg)
          move.l (a5),a3
          move.l (a3),d3
; This shouldn't have moved before nilreg cell is updated (even if fwd_token is
; in link word...)
          move.l vc_window_update_wptr(nilreg),a6
          cmp.l nilreg,a6
          if# ne then.s
           move.l v_data(a6),a6
           move.l a6,(a3)
           spush a6
           _EndUpdate
           move.l nilreg,vc_window_update_wptr(nilreg)
          endif#
          move.l evtMessage(sp),a6
          move.l a6,(a3)
          spush a6
          _BeginUpdate
          spush visRgn(a6)
          pea ltGray(a3)
          _FillRgn
          spush a6
          _EndUpdate
          move.l d3,(a3)
          bra @next_event
         endif#

In case you don't read assembler, the upshot is that it calls
#_EndUpdate if an update event is currently being processed, then it
does #_BeginUpdate, fills the update region with a gray pattern, and
does #_EndUpdate. If you are overriding MCL's
window-update-event-handler, you'll need to participate in the
protocol that tells the garbage collector which window is being
updated. Here's the MCL method:

(in-package :ccl)

(defmethod window-update-event-handler ((w window) &aux (wp (wptr w)))
  (let-globally ((*processing-events* t))       ; uninterruptable by events
    (with-focused-view w
      (let ((rgn (window-erase-region w))
            (invalid-rgn (window-invalid-region w)))
        (when rgn
          (#_InvalRgn rgn))
        (when *window-update-wptr*
          (#_EndUpdate *window-update-wptr*)
          (setq *window-update-wptr* nil))
        (unwind-protect
          (progn
            (#_BeginUpdate wp)
            (setq *window-update-wptr* wp)
            (when invalid-rgn
              (#_DiffRgn (rref wp :windowrecord.visRgn) invalid-rgn invalid-rgn)
              (if rgn
                (progn
                  (#_UnionRgn rgn invalid-rgn rgn)
                  (#_SetEmptyRgn invalid-rgn))
                (setq rgn invalid-rgn)))
            (when rgn
              (#_EraseRgn rgn)
              (#_SetEmptyRgn rgn))
            (view-draw-contents w))
          (when (setq wp *window-update-wptr*)
            (setq *window-update-wptr* nil)
            (#_EndUpdate wp)))))))

Note that the variable ccl::*window-update-wptr* is set to the WPTR
of the window being updated during the update. Your
window-update-event-handler should duplicate this behavior (and we
should document it). Note that #_EndUpdate is not called
unconditionally, in case the GC does the #_EndUpdate for you. I don't
know what will happen if #_EndUpdate is called twice or not at all,
but this may be causing your crashes.

If none of this helps, and you are willing to send me your code and
instructions for causing the crash, I'll try to figure out what's
going on. Any hint of a garbage collector bug makes me nervous and
eager to find and fix it.