*PRINT-CIRCLE* crosstalk between streams

Bug #309090 reported by Nikodemus Siivola
8
This bug affects 1 person
Affects Status Importance Assigned to Milestone
SBCL
Confirmed
Medium
Unassigned

Bug Description

  In sbcl-0.8.10.48 it's possible for *PRINT-CIRCLE* references to be
  mixed between streams when output operations are intermingled closely
  enough (as by doing output on S2 from within (PRINT-OBJECT X S1) in the
  test case below), so that e.g. the references #2# appears on a stream
  with no preceding #2= on that stream to define it (because the #2= was
  sent to another stream).
    (cl:in-package :cl-user)
    (defstruct foo index)
    (defparameter *foo* (make-foo :index 4))
    (defstruct bar)
    (defparameter *bar* (make-bar))
    (defparameter *tangle* (list *foo* *bar* *foo*))
    (defmethod print-object ((foo foo) stream)
      (let ((index (foo-index foo)))
        (format *trace-output*
         "~&-$- emitting FOO ~D, ambient *BAR*=~S~%"
         index *bar*)
        (format stream "[FOO ~D]" index))
      foo)
    (let ((tsos (make-string-output-stream))
          (ssos (make-string-output-stream)))
      (let ((*print-circle* t)
         (*trace-output* tsos)
         (*standard-output* ssos))
        (prin1 *tangle* *standard-output*))
      (let ((string (get-output-stream-string ssos)))
        (unless (string= string "(#1=[FOO 4] #S(BAR) #1#)")
          ;; In sbcl-0.8.10.48 STRING was "(#1=[FOO 4] #2# #1#)".:-(
          (error "oops: ~S" string)))))
  It might be straightforward to fix this by turning the
  *CIRCULARITY-HASH-TABLE* and *CIRCULARITY-COUNTER* variables into
  per-stream slots, but (1) it would probably be sort of messy faking
  up the special variable binding semantics using UNWIND-PROTECT and
  (2) it might be sort of a pain to test that no other bugs had been
  introduced.

Tags: printer
Changed in sbcl:
importance: Undecided → Medium
status: New → Confirmed
Revision history for this message
Siebe de Vos (s.de.vos) wrote :

Since today there is multi-threading, the *CIRCULARITY-HASH-TABLE* and *CIRCULARITY-COUNTER* can't be per stream slots. They need to be maintained per <thread,stream> combination.

It is debatable if this is a sufficient key, given that "22.4.19 *print-circle*" says:
"If a user-defined print-object method prints to a stream other than the one that was supplied, then circularity detection starts over for that stream."

'Starts over' taken literally, it would say that any user function used as print customization which writes to a different stream than the supplied one defines a new context.

Printing to the same stream from different contexts, whether dynamically scoped or via multiple threads has then undefined consequences.

Revision history for this message
Douglas Katzman (dougk) wrote :

Additionally, because the stream is not even an accurate indicator of whether a new top-level call has occurred, I don't know how circularity detection could ever be made reliable, if the "signal" to reset circularity detection is that the stream is different. Consider:
- FORMAT can introduce an intermediate stream when processing ~( ... ~)
- print-object methods could change from non-pretty printing (if that was the ambient value of *print-pretty*) to pretty, introducing a new instance of SB-PRETTY:PRETTY-STREAM
- any user-written output functions can introduce temporary streams (e.g. using WITH-OUTPUT-TO-STRING) before producing their final output

It's almost as if the outermost call to WRITE should been required to indicate that it is the outermost.

To post a comment you must log in.
This report contains Public information  
Everyone can see this information.

Other bug subscribers

Remote bug watches

Bug watches keep track of this bug in other bug trackers.