summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--eval.c18
-rw-r--r--share/txr/stdlib/yield.tl5
-rw-r--r--txr.155
-rw-r--r--unwind.c47
-rw-r--r--unwind.h1
5 files changed, 121 insertions, 5 deletions
diff --git a/eval.c b/eval.c
index 4c2f6411..052c7994 100644
--- a/eval.c
+++ b/eval.c
@@ -84,7 +84,7 @@ val setq_s, inc_s, zap_s;
val for_s, for_star_s, each_s, each_star_s, collect_each_s, collect_each_star_s;
val append_each_s, append_each_star_s, while_s, while_star_s, until_star_s;
val dohash_s;
-val uw_protect_s, return_s, return_from_s;
+val uw_protect_s, return_s, return_from_s, sys_abscond_from_s;
val list_s, append_s, apply_s, iapply_s;
val gen_s, gun_s, generate_s, rest_s, plus_s;
val promise_s, promise_forced_s, promise_inprogress_s, force_s;
@@ -1858,6 +1858,16 @@ static val op_return_from(val form, val env)
abort();
}
+static val op_abscond_from(val form, val env)
+{
+ val name = second(form);
+ val retval = eval(third(form), env, form);
+ uw_block_abscond(name, retval);
+ eval_error(form, lit("sys:abscond-from: no block named ~s is visible"),
+ name, nao);
+ abort();
+}
+
static val op_dwim(val form, val env)
{
val argexps = rest(form);
@@ -3030,7 +3040,9 @@ tail:
} else {
return rlcp(cons(sym, cons(funcs_ex, body_ex)), form);
}
- } else if (sym == block_s || sym == return_from_s) {
+ } else if (sym == block_s || sym == return_from_s ||
+ sym == sys_abscond_from_s)
+ {
val name = second(form);
val body = rest(rest(form));
val body_ex = expand_progn(body, menv);
@@ -4157,6 +4169,7 @@ void eval_init(void)
uw_protect_s = intern(lit("unwind-protect"), user_package);
return_s = intern(lit("return"), user_package);
return_from_s = intern(lit("return-from"), user_package);
+ sys_abscond_from_s = intern(lit("abscond-from"), system_package);
gethash_s = intern(lit("gethash"), user_package);
car_s = intern(lit("car"), user_package);
cdr_s = intern(lit("cdr"), user_package);
@@ -4252,6 +4265,7 @@ void eval_init(void)
reg_op(block_s, op_block);
reg_op(return_s, op_return);
reg_op(return_from_s, op_return_from);
+ reg_op(sys_abscond_from_s, op_abscond_from);
reg_op(dwim_s, op_dwim);
reg_op(quasi_s, op_quasi_lit);
reg_op(catch_s, op_catch);
diff --git a/share/txr/stdlib/yield.tl b/share/txr/stdlib/yield.tl
index 0a623a2b..ef0518e6 100644
--- a/share/txr/stdlib/yield.tl
+++ b/share/txr/stdlib/yield.tl
@@ -51,8 +51,9 @@
(let ((cont-sym (gensym)))
^(sys:yield-impl ',name
(lambda (,cont-sym)
- (return-from ,name (new (sys:yld-item
- ,form (cdr ,cont-sym)))))
+ (sys:abscond-from ,name
+ (new (sys:yld-item
+ ,form (cdr ,cont-sym)))))
',ctx-form)))
(defmacro yield (form)
diff --git a/txr.1 b/txr.1
index de1430f6..23958b86 100644
--- a/txr.1
+++ b/txr.1
@@ -27413,6 +27413,25 @@ and
which create an abstraction which models the continuation as a suspended
procedure supporting two-way communication of data.
+Continuations raise the issue of what to do about unwinding.
+The language Scheme provides the much criticized
+.code dynamic-wind
+operator which can execute initialization and clean-up code as
+a continuation is entered and abandoned. \*(TX takes a simpler,
+albeit risky approach. It provides a non-unwinding escape operator
+.code sys:abscond-from
+for use with continuations. Code which has captured a continuation
+can use this operator to escape from the delimiting block without
+triggering any unwinding among the frames between the capture point and the
+delimiter. When the continuation is restarted, it will then do so
+with all of the resources associated with it frames intact.
+When the continuation executes normal returns within its context,
+the unwinding takes place then. Thus tidy, "thread-like" use
+of continuations is possible with a small measure of coding discipline.
+Unfortunately, the absconding operator is dangerous: its use
+breaks the language guarantee that clean-up associated with a form is done no
+matter how a form terminates.
+
.TP* Notes:
Delimited continuations resemble lexical closures in some ways. Both
@@ -27548,6 +27567,37 @@ with named prompts.
--> 24
.cble
+.coNP Operator @ sys:abscond-from
+.synb
+.mets (sys:abscond-from < name <> [ value ])
+.syne
+.desc
+The
+.code sys:abscond-from
+operator closely resembles
+.code return-from
+and performs the same thing: it causes an enclosing block
+.meta name
+to terminate with
+.meta value
+which defaults to
+.codn nil .
+
+However, unlike
+.codn return-from ,
+.code sys:abscond-from
+does not perform any unwinding.
+
+This operator should never be used for any purpose other than
+implementing primitives for the use of delimited continuations.
+It is used by the
+.code yield-from
+and
+.code yield
+operators to escape out of a block in which a continuation has
+been captured. Neglecting to unwind is valid due to the expectation
+that control will return into a restarted copy of that context.
+
.coNP Macros @ obtain and @ yield-from
.synb
.mets (obtain << forms *)
@@ -27573,7 +27623,10 @@ are encapsulated in a special
Finally,
.code yield-from
performs a non-local transfer to the same block, so that the yield object
-appears as the result value of that block.
+appears as the result value of that block. The non-local transfer is
+performed abruptly, by the
+.code sys:abscond-from
+operator.
An
.code obtain
diff --git a/unwind.c b/unwind.c
index fe3c68b6..c679c54e 100644
--- a/unwind.c
+++ b/unwind.c
@@ -114,6 +114,34 @@ static void uw_unwind_to_exit_point(void)
}
}
+static void uw_abscond_to_exit_point(void)
+{
+ assert (uw_exit_point);
+
+ for (; uw_stack && uw_stack != uw_exit_point; uw_stack = uw_stack->uw.up) {
+ switch (uw_stack->uw.type) {
+ case UW_ENV:
+ uw_env_stack = uw_env_stack->ev.up_env;
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (!uw_stack)
+ abort();
+
+ uw_exit_point = 0;
+
+ switch (uw_stack->uw.type) {
+ case UW_BLOCK:
+ extended_longjmp(uw_stack->bl.jb, 1);
+ abort();
+ default:
+ abort();
+ }
+}
+
void uw_push_block(uw_frame_t *fr, val tag)
{
memset(fr, 0, sizeof *fr);
@@ -356,6 +384,25 @@ val uw_block_return_proto(val tag, val result, val protocol)
abort();
}
+val uw_block_abscond(val tag, val result)
+{
+ uw_frame_t *ex;
+
+ for (ex = uw_stack; ex != 0; ex = ex->uw.up) {
+ if (ex->uw.type == UW_BLOCK && ex->bl.tag == tag)
+ break;
+ }
+
+ if (ex == 0)
+ return nil;
+
+ ex->bl.result = result;
+ ex->bl.protocol = nil;
+ uw_exit_point = ex;
+ uw_abscond_to_exit_point();
+ abort();
+}
+
void uw_push_catch(uw_frame_t *fr, val matches)
{
memset(fr, 0, sizeof *fr);
diff --git a/unwind.h b/unwind.h
index a9b456ae..bcf8d185 100644
--- a/unwind.h
+++ b/unwind.h
@@ -102,6 +102,7 @@ INLINE val uw_block_return(val tag, val result)
{
return uw_block_return_proto(tag, result, nil);
}
+val uw_block_abscond(val tag, val result);
void uw_push_catch(uw_frame_t *, val matches);
void uw_push_handler(uw_frame_t *, val matches, val fun);
noreturn val uw_throw(val sym, val exception);