Skip to content

Commit afc2c13

Browse files
committed
Improve refcount for continuations
1 parent d3b244c commit afc2c13

File tree

4 files changed

+84
-36
lines changed

4 files changed

+84
-36
lines changed

lib/std/core/hnd.kk

Lines changed: 29 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -603,31 +603,37 @@ fun get( ref: ref<h,a>) : <read<h>,div> a
603603
inline extern unsafe-st(f : () -> <st<global>|e> a ) : (() -> e a)
604604
inline "#1"
605605

606-
fun protect-prompt( resumed : ref<global,bool>, k : resume-result<b,r> -> e r, res : r ) : e r
607-
val did-resume : bool = (unsafe-st{ !resumed })()
608-
if did-resume then
609-
// if resumed, we no longer need to protect
610-
res
611-
elif !yielding() then
612-
// otherwise, if we are not yielding, resume k with finalization (to run all finally clauses)
613-
k(Finalize(res))
614-
elif yielding-non-final() then
615-
// if we yield non-final to an operation, extend the continuation with this prompt (so we keep protecting after being resumed)
616-
yield-cont( fn(cont,x) protect-prompt(pretend-decreasing(resumed),k,cont(x)) )
617-
else
618-
// if we are in a final yield, capture it, resume k with finalization, and reyield
619-
val yld = yield-capture()
620-
k(Finalize(res))
621-
if yielding() return yield-extend( fn(_x) unsafe-reyield(yld) ) // yikes, a finally clause is itself yielding...
622-
unsafe-reyield(yld)
606+
type protect-state<b,r,e>
607+
NeedsFinalization(k : resume-result<b,r> -> e r)
608+
NoFinalization
609+
610+
fun protect-prompt( resumed : ref<global,protect-state<b,r,e>>, res : r) : e r
611+
val hdiv=@Hnodiv
612+
val did-resume : some<b,r,e> protect-state<b,r,e> = (unsafe-st{ !resumed })()
613+
match did-resume
614+
NoFinalization -> // resumed already, no need to protect
615+
res
616+
NeedsFinalization(k) ->
617+
if !yielding() then
618+
// otherwise, if we are not yielding, resume k with finalization (to run all finally clauses)
619+
k(Finalize(res))
620+
elif yielding-non-final() then
621+
// if we yield non-final to an operation, extend the continuation with this prompt (so we keep protecting after being resumed)
622+
yield-cont( fn(cont,x) protect-prompt(pretend-decreasing(resumed),cont(x)) )
623+
else
624+
// if we are in a final yield, capture it, resume k with finalization, and reyield
625+
val yld = yield-capture()
626+
k(Finalize(res))
627+
if yielding() return yield-extend( fn(_x) unsafe-reyield(yld) ) // yikes, a finally clause is itself yielding...
628+
unsafe-reyield(yld)
623629

624630
fun protect( x : a, clause : (x:a, k: b -> e r) -> e r, k : resume-result<b,r> -> e r ) : e r
625-
val resumed = (unsafe-st{ref(False)})()
631+
val resumed = (unsafe-st{ref(NeedsFinalization(k))})()
626632
fun kprotect(ret)
627-
(unsafe-st{resumed := True})()
633+
(unsafe-st{resumed := NoFinalization})()
628634
k(Deep(ret))
629635
val res = clause(x,kprotect)
630-
protect-prompt(resumed,k,res)
636+
protect-prompt(resumed,res)
631637

632638
/*
633639
pub fun clause-control1( clause : (x:a, k: b -> e r) -> e r ) : clause1<a,b,e,r>
@@ -715,12 +721,12 @@ fun under2( ev : ev<h>, op : (a1,a2) -> e b, x1 : a1, x2 : a2 ) : e b
715721
z
716722

717723
fun protect2( x1 : a1, x2:a2, clause : (x:a1,x:a2, k: b -> e r) -> e r, k : resume-result<b,r> -> e r ) : e r
718-
val resumed = (unsafe-st{ref(False)})()
724+
val resumed = (unsafe-st{ref(NeedsFinalization(k))})()
719725
fun kprotect(ret)
720-
(unsafe-st{ resumed := True })()
726+
(unsafe-st{ resumed := NoFinalization })()
721727
k(Deep(ret))
722728
val res = clause(x1,x2,kprotect)
723-
protect-prompt(resumed,k,res)
729+
protect-prompt(resumed,res)
724730

725731
pub fun clause-control2( clause : (x1:a1, x2:a2, k: b -> e r) -> e r ) : clause2<a1,a2,b,h,e,r>
726732
Clause2(fn(m,_ev,x1,x2){ yield-to(m, fn(k){ protect2(x1,x2,clause,k) }) })

lib/std/core/inline/hnd.c

Lines changed: 31 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -268,22 +268,40 @@ static kk_box_t kcompose( kk_function_t fself, kk_box_t x, kk_context_t* ctx) {
268268
kk_intx_t count = kk_intf_unbox(self->count);
269269
kk_function_t* conts = &self->conts[0];
270270
// call each continuation in order
271-
for(kk_intx_t i = 0; i < count; i++) {
272-
// todo: take uniqueness of fself into account to avoid dup_function
273-
kk_function_t f = kk_function_dup(conts[i],ctx);
274-
x = kk_function_call(kk_box_t, (kk_function_t, kk_box_t, kk_context_t*), f, (f, x, ctx), ctx);
275-
if (kk_yielding(ctx)) {
276-
// if yielding, `yield_next` all continuations that still need to be done
277-
while(++i < count) {
278-
// todo: if fself is unique, we could copy without dup?
279-
kk_yield_extend(kk_function_dup(conts[i],ctx),ctx);
271+
if kk_likely(kk_datatype_ptr_is_unique(fself, ctx)) {
272+
// Special handling for unique continuation function to avoid dup/drop overhead for the continuation and captured variables.
273+
for(kk_intx_t i = 0; i < count; i++) {
274+
kk_function_t f = conts[i];
275+
x = kk_function_call(kk_box_t, (kk_function_t, kk_box_t, kk_context_t*), f, (f, x, ctx), ctx);
276+
if (kk_yielding(ctx)) {
277+
// if yielding, `yield_next` all continuations that still need to be done
278+
while(++i < count) {
279+
kk_yield_extend(conts[i],ctx); // just move the continuation (no dup needed since it's parent is unique and being dropped)
280+
}
281+
kk_free((void*)self, ctx);
282+
kk_box_drop(x,ctx); // still drop even though we yield as it may release a boxed value type?
283+
return kk_box_any(ctx); // return yielding
280284
}
281-
kk_function_drop(fself,ctx);
282-
kk_box_drop(x,ctx); // still drop even though we yield as it may release a boxed value type?
283-
return kk_box_any(ctx); // return yielding
284285
}
286+
kk_free((void*)self, ctx);
287+
// kk_function_drop(self,ctx); Can't do this, since all of it's child functions are dropped!
288+
} else {
289+
for(kk_intx_t i = 0; i < count; i++) {
290+
// todo: take uniqueness of fself into account to avoid dup_function
291+
kk_function_t f = kk_function_dup(conts[i],ctx);
292+
x = kk_function_call(kk_box_t, (kk_function_t, kk_box_t, kk_context_t*), f, (f, x, ctx), ctx);
293+
if (kk_yielding(ctx)) {
294+
// if yielding, `yield_next` all continuations that still need to be done
295+
while(++i < count) {
296+
kk_yield_extend(kk_function_dup(conts[i],ctx),ctx);
297+
}
298+
kk_function_drop(fself,ctx);
299+
kk_box_drop(x,ctx); // still drop even though we yield as it may release a boxed value type?
300+
return kk_box_any(ctx); // return yielding
301+
}
302+
}
303+
kk_function_drop(fself,ctx);
285304
}
286-
kk_function_drop(fself,ctx);
287305
return x;
288306
}
289307

test/cgen/simple-refcount.kk

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import std/num/int32
2+
3+
extern create-box(): io-noexn any
4+
c inline "kk_cptr_raw_box(&kk_free_fun, kk_malloc(sizeof(int), _ctx), _ctx)"
5+
6+
extern get-refcount(^box: any): io-noexn int32
7+
c inline "kk_block_refcount(kk_box_to_ptr(box, _ctx))"
8+
9+
effect ctl catch(): ()
10+
11+
fun main()
12+
with handler ctl catch()
13+
resume(())
14+
resume(())
15+
val box = create-box()
16+
val count0 = box.get-refcount()
17+
catch()
18+
val count1 = box.get-refcount()
19+
println((count0.int, count1.int).show)
20+
// Expected output:
21+
// (1,1)
22+
// (1,0)

test/cgen/simple-refcount.kk.out

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
(1,1)
2+
(1,0)

0 commit comments

Comments
 (0)