diff options
author | Kaz Kylheku <kaz@kylheku.com> | 2021-08-04 20:52:10 -0700 |
---|---|---|
committer | Kaz Kylheku <kaz@kylheku.com> | 2021-08-04 20:52:10 -0700 |
commit | 29b5a31ad6d3783e9bb178f35d21110bea06451e (patch) | |
tree | 4a19b7113c79b0d80e93c7501db45aac9fe9c1f8 | |
parent | 75c6845ef1fc840afe995a84f3cd1c94f5257d7d (diff) | |
download | txr-29b5a31ad6d3783e9bb178f35d21110bea06451e.tar.gz txr-29b5a31ad6d3783e9bb178f35d21110bea06451e.tar.bz2 txr-29b5a31ad6d3783e9bb178f35d21110bea06451e.zip |
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.
-rw-r--r-- | stream.c | 16 | ||||
-rw-r--r-- | stream.h | 1 | ||||
-rw-r--r-- | txr.1 | 10 |
3 files changed, 17 insertions, 10 deletions
@@ -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) @@ -55,6 +55,7 @@ struct strm_base { unsigned force_break; cnum max_length; cnum max_depth; + val close_result; struct strm_ctx *ctx; }; @@ -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 |