summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--autoload.c1
-rw-r--r--eval.c2
-rw-r--r--lib.c22
-rw-r--r--lib.h1
-rw-r--r--stdlib/doc-syms.tl1
-rw-r--r--stdlib/getput.tl6
-rw-r--r--txr.170
7 files changed, 102 insertions, 1 deletions
diff --git a/autoload.c b/autoload.c
index 66479326..c8db544b 100644
--- a/autoload.c
+++ b/autoload.c
@@ -473,6 +473,7 @@ static val getput_set_entries(val fun)
lit("command-get-buf"), lit("command-put-buf"),
lit("command-get-json"), lit("command-put-json"),
lit("command-get-jsons"), lit("command-put-jsons"),
+ lit("close-lazy-streams"),
nil
};
autoload_set(al_fun, name, fun);
diff --git a/eval.c b/eval.c
index 69dc238b..0bcbd469 100644
--- a/eval.c
+++ b/eval.c
@@ -7486,6 +7486,8 @@ void eval_init(void)
reg_fun(intern(lit("rt-assert-fail"), system_package), func_n4ov(rt_assert_fail, 3));
+ reg_var(lazy_streams_s, nil);
+
eval_error_s = intern(lit("eval-error"), user_package);
case_error_s = intern(lit("case-error"), user_package);
uw_register_subtype(eval_error_s, error_s);
diff --git a/lib.c b/lib.c
index 13ac61ad..fbb5d9e8 100644
--- a/lib.c
+++ b/lib.c
@@ -120,6 +120,7 @@ val path_not_found_s, path_exists_s, path_permission_s;
val warning_s, defr_warning_s, restart_s, continue_s;
val gensym_counter_s, length_s;
val rplaca_s, rplacd_s, seq_iter_s;
+val lazy_streams_s;
val nothrow_k, args_k, colon_k, auto_k, fun_k;
val wrap_k, reflect_k;
@@ -145,6 +146,8 @@ struct cobj_class *seq_iter_cls;
static val recycled_conses;
+static val lazy_streams_binding;
+
const seq_kind_t seq_kind_tab[MAXTYPE+1] = {
SEQ_NIL, /* NIL */
SEQ_NOTSEQ, /* NUM */
@@ -9516,6 +9519,20 @@ static val lazy_stream_func_nt(val env, val lcons)
return prefetched_line;
}
+static void lazy_stream_register(val stream)
+{
+ val lazy_streams_dyn_binding = lookup_var(nil, lazy_streams_s);
+
+ if (lazy_streams_dyn_binding) {
+ if (!lazy_streams_binding)
+ lazy_streams_binding = lookup_global_var(lazy_streams_s);
+
+ if (lazy_streams_dyn_binding != lazy_streams_binding) {
+ val list = us_cdr(lazy_streams_dyn_binding);
+ us_rplacd(lazy_streams_dyn_binding, cons(stream, list));
+ }
+ }
+}
val lazy_stream_cons(val stream, val no_throw_close)
{
@@ -9523,6 +9540,7 @@ val lazy_stream_cons(val stream, val no_throw_close)
no_throw_close = default_null_arg(no_throw_close);
if (real_time_stream_p(stream)) {
+ lazy_stream_register(stream);
return make_lazy_cons(func_f1(stream, if3(no_throw_close,
simple_lazy_stream_func_nt,
simple_lazy_stream_func)));
@@ -9534,6 +9552,7 @@ val lazy_stream_cons(val stream, val no_throw_close)
return nil;
}
+ lazy_stream_register(stream);
return make_lazy_cons(func_f1(cons(stream, first),
if3(no_throw_close,
lazy_stream_func_nt,
@@ -13243,7 +13262,7 @@ static void obj_init(void)
&equal_f, &eq_f, &eql_f,
&car_f, &cdr_f, &null_f, &list_f,
&identity_f, &identity_star_f, &less_f, &greater_f,
- &prog_string, &cobj_hash,
+ &prog_string, &cobj_hash, &lazy_streams_binding,
convert(val *, 0));
nil_string = lit("nil");
@@ -13387,6 +13406,7 @@ static void obj_init(void)
rplaca_s = intern(lit("rplaca"), user_package);
rplacd_s = intern(lit("rplacd"), user_package);
seq_iter_s = intern(lit("seq-iter"), user_package);
+ lazy_streams_s = intern(lit("*lazy-streams*"), system_package);
args_k = intern(lit("args"), keyword_package);
nothrow_k = intern(lit("nothrow"), keyword_package);
diff --git a/lib.h b/lib.h
index 2e506e45..2cfb714a 100644
--- a/lib.h
+++ b/lib.h
@@ -560,6 +560,7 @@ extern val path_not_found_s, path_exists_s, path_permission_s;
extern val warning_s, defr_warning_s, restart_s, continue_s;
extern val gensym_counter_s, length_s;
extern val rplaca_s, rplacd_s, seq_iter_s;
+extern val lazy_streams_s;
#define gensym_counter (deref(lookup_var_l(nil, gensym_counter_s)))
diff --git a/stdlib/doc-syms.tl b/stdlib/doc-syms.tl
index 5d056427..e9685e70 100644
--- a/stdlib/doc-syms.tl
+++ b/stdlib/doc-syms.tl
@@ -338,6 +338,7 @@
("clock-t" "N-01B6F219")
("clockid-t" "N-01153D9E")
("close" "D-0016")
+ ("close-lazy-streams" "N-00B8ACD5")
("close-stream" "N-00596930")
("closedir" "N-01FEE88A")
("closelog" "N-02CEE7EA")
diff --git a/stdlib/getput.tl b/stdlib/getput.tl
index 0cf751a1..a1be729e 100644
--- a/stdlib/getput.tl
+++ b/stdlib/getput.tl
@@ -187,3 +187,9 @@
(defun command-put-jsons (cmd seq : flat-p mopt)
(with-stream (s (open-command cmd `w@mopt`))
(put-jsons seq s flat-p)))
+
+(defmacro close-lazy-streams (. body)
+ ^(let ((sys:*lazy-streams*))
+ (unwind-protect
+ (progn ,*body))
+ (mapdo (fun close-stream) sys:*lazy-streams*)))
diff --git a/txr.1 b/txr.1
index c51b8d96..692a059f 100644
--- a/txr.1
+++ b/txr.1
@@ -23796,6 +23796,76 @@ is true, then
.meta throw-on-error-p
shall be false, and vice versa.
+.coNP Macro @ close-lazy-streams
+.synb
+.mets (close-lazy-streams << body-form *)
+.syne
+.desc
+The
+.code close-lazy-streams
+macro establishes a dynamic environment in which zero or more
+.metn body-form s
+are evaluated, yielding the value of the last
+.metn body-form ,
+or else
+.code nil
+if there are no
+.meta body-form
+arguments. In this regard, the macro operator resembles
+.codn progn .
+
+The environment established by
+.code close-lazy-streams
+sets up special monitoring of the the functions
+.code lazy-stream-cons
+and
+.codn get-lines .
+Whenever these functions register an I/O stream with a lazy list, that stream is
+recorded in a hidden. When the
+.code close-lazy-streams
+form terminates, it invokes the
+.code close-stream
+on each stream in the hidden list.
+
+Note: the
+.code close-lazy-streams
+macro provides a possible solution for situations in which a body of code,
+possibly consisting of nested functions, manipulates lazy lists of lines coming
+from from I/O streams, such that these lists are not completely forced.
+Incompletely processed lazy lists will not close their associated streams until
+they are reclaimed by garbage collection, which could cause the application to
+run out of file descriptors.
+The
+.code close-lazy-streams
+macro allows the application to delineate a dynamic contour of code
+upon whose termination all such stream associations generated within
+that contour will be duly cleaned up.
+
+.TP* Example:
+
+Collect list of names of
+.code .tl
+files which contain the string
+.strn "(cons " :
+
+.verb
+ ;; Incorrect version: could run out of open files if there are many
+ ;; files which contain a match processed, because find-if will stop
+ ;; traversing the list of lines when it finds a match:
+ (build
+ (each ((file (glob "*.tl")))
+ (if (find-if #/\e(cons / (file-get-lines file))
+ (add file))))
+
+ ;; Addressed with close-lazy-streams: after each iteration, the
+ ;; stream created by file-get-lines is closed.
+ (build
+ (each ((file (glob "*.tl")))
+ (close-lazy-streams
+ (if (find-if #/\e(cons / (file-get-lines file))
+ (add file)))))
+.brev
+
.coNP Macro @ delay
.synb
.mets (delay << expression )