From e74fae44289111e2ae3707906d3311f17c8f7b25 Mon Sep 17 00:00:00 2001 From: Kaz Kylheku Date: Wed, 4 Aug 2021 20:52:10 -0700 Subject: close-stream: make idemponent. * stream.c (strm_base_init): Add new element to the initializer to initialize the close_result member to nao, indicating that the close operation has not been invoked. (strm_base_mark): Mark the close_result value, if it isn't nao. This is just in case it is a heap object. The structure delegate mechanism opens the possibility that the stream is actually user code that can return anything so we have to be careful. (close_stream): Only call ops->close if close_result is nao, indicating that close had never been called (or possibly that it had been called bu threw an exception) and store the return value in close_result, otherwise return the previously stored value. * stream.h (struct strm_base): New member, close_result. * txr.1: Documented. --- stream.c | 16 +++++++++++----- stream.h | 1 + txr.1 | 10 +++++----- 3 files changed, 17 insertions(+), 10 deletions(-) diff --git a/stream.c b/stream.c index a1f5ead2..3f2cafb3 100644 --- a/stream.c +++ b/stream.c @@ -109,7 +109,7 @@ static val shell, shell_arg; void strm_base_init(struct strm_base *s) { - static struct strm_base init = { indent_off, 60, 10, 0, 0, 0, 0, 0, 0 }; + static struct strm_base init = { indent_off, 60, 10, 0, 0, 0, 0, 0, nao, 0 }; *s = init; } @@ -120,7 +120,8 @@ void strm_base_cleanup(struct strm_base *s) void strm_base_mark(struct strm_base *s) { - (void) s; + if (s->close_result != nao) + gc_mark(s->close_result); } void stream_print_op(val stream, val out, val pretty, struct strm_ctx *ctx) @@ -2971,9 +2972,14 @@ val real_time_stream_p(val obj) val close_stream(val stream, val throw_on_error) { val self = lit("close-stream"); - struct strm_ops *ops = coerce(struct strm_ops *, - cobj_ops(self, stream, stream_cls)); - return ops->close(stream, 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); + + if (s->close_result == nao) + s->close_result = ops->close(stream, throw_on_error); + + return s->close_result; } val get_error(val stream) diff --git a/stream.h b/stream.h index 3b9db2b3..e81dc8c1 100644 --- a/stream.h +++ b/stream.h @@ -55,6 +55,7 @@ struct strm_base { unsigned force_break; cnum max_length; cnum max_depth; + val close_result; struct strm_ctx *ctx; }; diff --git a/txr.1 b/txr.1 index 280aff16..b4fe9a7f 100644 --- a/txr.1 +++ b/txr.1 @@ -56709,13 +56709,13 @@ instead of returning If .code close-stream -is applied to a +is called in such a way that it returns a value, without throwing an exception, +that value is retained. Additional calls to the function with the same .meta stream -which is already closed, the operation returns -.code nil -regardless of the +object return that same value without having any effect on the stream. +These additional calls ignore the .meta throw-on-error-p -argument's value. +argument. .coNP Macro @ with-stream .synb -- cgit v1.2.3