[Openmcl-devel] duplicating a stream

Gary Byers gb at clozure.com
Mon Mar 26 23:12:00 UTC 2012

See below.

On Mon, 26 Mar 2012, Pascal J. Bourguignon wrote:

> When calling run-program, one need to pass a :shared stream.
> Since I may get non shared file streams (by default they're :private),
> I'd want to "duplicate" the stream, making a shared stream to give to
> run-program, using the same stream-device:
>  (defun wrap-stream (direction stream)
>    ;; Since stream may not be shared, we make a new stream for
>    ;; the process.
>    (typecase stream
>      (stream     (ccl::make-basic-stream-instance
>                   (if (eql :input direction)
>                       'file-character-input-stream
>                       'file-character-output-stream)
>                   :stream-device (ccl::stream-device stream direction)
>                   :direction direction
>                   :element-type 'character
>                   :sharing :lock
>                   :encoding ccl:*default-file-character-encoding*
>                   :line-termination #+windows :windows #-windows :unix
>                   :auto-close t))
>      (otherwise  stream)))
> Unfortunately that fails with:
>    There is no applicable method for the generic function:
>      #<standard-generic-function ccl:class-own-wrapper #x302000032F0F>
>    when called with arguments:
>      (com.informatimago.run-program::file-character-output-stream)
>       [Condition of type simple-error]
> What did I do wrong?

1) called some random internal CCL function.
1a) called that function with the wrong type of arguments (the first
     arg is expected to be a CLASS object, not a symbol that names a class)
1b) called that function with symbols that don't even name classes (though
    symbols with the same pnames that're internal to the CCL package do
    happen to name classes).
    UPDATE: you get some points back for noticing this, but I'm afraid that
    that barely matters.
2) decided that this should happen if your function's argument is of type STREAM.
    (STRING-STREAMs and lots of other things are of type STREAM.)
3) Think that creating a situation where two lisp streams share the same file
    descriptor would be a good idea.  (There might be cases where this is useful,
    but as a general rule I think it's fair to say that this isn't a good idea.)
4) In CCL (and most other programming languages), streams are often buffered;
    the "state" (position, set of characters/bytes read/written, etc.) of the
    stream and that of the underlying file descriptor are related to each other
    but aren't generally identical.  If it made sense to "duplicate a stream",
    creating another stream that uses the same fd would only be a small part
    of that.
5) That's enough for now.

Until an hour or two ago, the documentation said that a stream created with
:SHARING :PRIVATE could only be used by the thread that created it.  That
hasn't been true in several years: such a stream is "owned" by whatever
thread first tries to do I/O on the stream, so common idioms like:

(with-open-file (f path)
   (process-run-function "something" (lamba () (print (read-line f))))
   (sleep long-enough-to-let-the-thread-run))

work as expected.  (I don't claim that that's a plausible-looking example,
but I think that the idiom's indeed fairly common.)

The case that I can think of where this (stream sharing/ownership) matters
involves things like:

(with-open-file (f "home:log.txt" :sharing :private :direction :output :if-does-not-exist :create :if-exists :superseded)
   (write-line "Copyright (c) 2012 Acme Corporation." f) ; become stream's "owner"
   (run-program "/bin/echo" '("yow!")  :output f))

(I think that some versions of a widely-used CL logging package run into this or
something similar.)  If this can't be made to work, it'd probably be better
to complain about the argument earlier; in this particular case - where the stream
is essentially thread-private except for the fact that a thread used in the
implementation of RUN-PROGRAM wants to write to it) there may be ways to avoid
the error (possibly by temporarily transferring ownership of the stream to
the  background process.)

> -- 
> __Pascal Bourguignon__                     http://www.informatimago.com/
> A bad day in () is better than a good day in {}.
> _______________________________________________
> Openmcl-devel mailing list
> Openmcl-devel at clozure.com
> http://clozure.com/mailman/listinfo/openmcl-devel

More information about the Openmcl-devel mailing list