summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKaz Kylheku <kaz@kylheku.com>2023-08-07 20:10:54 -0700
committerKaz Kylheku <kaz@kylheku.com>2023-08-07 20:10:54 -0700
commit44453f30dd81ffa1e995662fd75a675b5b208406 (patch)
treebe5b301f1899521e63e6d9d1c6facdcd3dbf77ce
parentc46898c74e39fc56e67e5bc03c574a3144362634 (diff)
downloadtxr-44453f30dd81ffa1e995662fd75a675b5b208406.tar.gz
txr-44453f30dd81ffa1e995662fd75a675b5b208406.tar.bz2
txr-44453f30dd81ffa1e995662fd75a675b5b208406.zip
close-stream: new : protocol from close method.
* stream.c (close_stream): If the underlying method returns the colon symbol :, then keep the cached close_result as nil, so that the method can be called again, but return t to the caller to indicate success. * tests/018/close-delegate.tl: Test case added. * tests/018/close-delegate.expected: Updated. * txr.1: Documented.
-rw-r--r--stream.c13
-rw-r--r--tests/018/close-delegate.expected5
-rw-r--r--tests/018/close-delegate.tl20
-rw-r--r--txr.136
4 files changed, 70 insertions, 4 deletions
diff --git a/stream.c b/stream.c
index 4c112052..f948e988 100644
--- a/stream.c
+++ b/stream.c
@@ -2990,11 +2990,18 @@ val close_stream(val stream, val throw_on_error)
struct strm_base *s = coerce(struct strm_base *,
cobj_handle(self, stream, stream_cls));
struct strm_ops *ops = coerce(struct strm_ops *, stream->co.ops);
+ val res = s->close_result;
- if (!s->close_result)
- s->close_result = ops->close(stream, throw_on_error);
+ if (!res) {
+ res = ops->close(stream, throw_on_error);
- return s->close_result;
+ if (res == colon_k)
+ res = t;
+ else if (res)
+ s->close_result = res;
+ }
+
+ return res;
}
val get_error(val stream)
diff --git a/tests/018/close-delegate.expected b/tests/018/close-delegate.expected
index 227f9f67..de68447f 100644
--- a/tests/018/close-delegate.expected
+++ b/tests/018/close-delegate.expected
@@ -1,3 +1,6 @@
close called, count 2
close called, count 1
-20
+40
+close called, count 2
+close called, count 1
+40
diff --git a/tests/018/close-delegate.tl b/tests/018/close-delegate.tl
index 4cf1d650..64a1dc91 100644
--- a/tests/018/close-delegate.tl
+++ b/tests/018/close-delegate.tl
@@ -18,3 +18,23 @@
(get-lines s))
len
prinl)
+
+(defstruct refcount-close-alt stream-wrap
+ stream
+ (count 1)
+
+ (:method close (me throw-on-error-p)
+ (put-line `close called, count @{me.count}`)
+ (when (plusp me.count)
+ (if (zerop (dec me.count))
+ (close-stream me.stream throw-on-error-p)
+ :))))
+
+(flow
+ (with-stream (s (make-struct-delegate-stream
+ (new refcount-close-alt
+ count 2
+ stream (open-file *load-path*))))
+ (get-lines s))
+ len
+ prinl)
diff --git a/txr.1 b/txr.1
index fb409b16..373f7547 100644
--- a/txr.1
+++ b/txr.1
@@ -65783,6 +65783,42 @@ description of the
.code close-stream
stream I/O function.
+With two exceptions, the value returned from
+.code close
+is retained by close-stream, such that repeated calls to
+.code close-stream
+then return that value without calling the
+.code close
+method.
+The exceptions are the values
+.code nil
+and
+.code :
+(the colon symbol).
+If either of these values is returned, and
+.code close-stream
+is invoked again on the same stream object, the
+.code close
+method will be called again.
+
+Furthermore, if the
+.code :
+symbol is returned by the
+.code close
+method, this indicates a successful close, and the
+.code close-stream
+function returns the
+.code t
+symbol rather than the
+.code :
+symbol.
+
+The rationale for this mechanism is that it supports reference-counted
+closing. A struct delegate stream may be written which is shared by
+several owners, which must each call
+.code close-stream
+before the underlying real stream is closed.
+
.coNP Method @ flush
.synb
.mets << stream .(flush < offs << whence )