1616priv struct EventLoop {
1717 subscribes : Map [Int , Subscriber ]
1818 tasks : Map [WaitableSet , Coroutine ]
19+ owned_coroutines : Map [WaitableSet , @set.Set [Coroutine ]]
1920 finished : Map [WaitableSet , Bool ]
2021}
2122
@@ -39,6 +40,7 @@ fn should_complete(waitable_set : WaitableSet) -> Bool {
3940fn next_callback(waitable_set : WaitableSet ) -> Int {
4041 if should_complete(waitable_set) {
4142 ev.tasks.remove(waitable_set)
43+ ev.owned_coroutines.remove(waitable_set)
4244 ev.finished.remove(waitable_set)
4345 waitable_set.drop()
4446 return CallbackCode ::Completed .encode()
@@ -52,12 +54,17 @@ fn next_callback(waitable_set : WaitableSet) -> Int {
5254///|
5355pub fn with_waitableset(f : async () -> Unit ) -> Int {
5456 let waitable_set = WaitableSet ::new()
57+ let owned = @set.Set ::new()
5558 tls_set(waitable_set.0)
5659 let coro = spawn(async fn() -> Unit {
60+ let coro = current_coroutine()
61+ defer owned.remove(coro)
5762 defer ev.finished.set(waitable_set, true )
5863 f()
5964 })
6065 ev.tasks.set(waitable_set, coro)
66+ owned.add(coro)
67+ ev.owned_coroutines.set(waitable_set, owned)
6168 ev.finished.set(waitable_set, false )
6269 reschedule()
6370 next_callback(waitable_set)
@@ -75,13 +82,17 @@ pub fn cb(event : Int, waitable_id : Int, code : Int) -> Int {
7582 TaskCancelled => {
7683 guard ev.tasks.get(waitable_set) is Some (coro)
7784 coro.cancel()
78- for {
85+ if ev.owned_coroutines.get(waitable_set) is Some (owned) {
86+ owned.each(Coroutine ::cancel)
87+ }
88+ for ;; {
7989 reschedule()
8090 if !has_immediately_ready_task() {
8191 break
8292 }
8393 }
8494 ev.tasks.remove(waitable_set)
95+ ev.owned_coroutines.remove(waitable_set)
8596 ev.finished.remove(waitable_set)
8697 if ev.subscribes.is_empty() {
8798 waitable_set.drop()
@@ -104,7 +115,32 @@ pub fn cb(event : Int, waitable_id : Int, code : Int) -> Int {
104115}
105116
106117///|
107- let ev : EventLoop = { subscribes: {}, tasks: {}, finished: {} }
118+ let ev : EventLoop = {
119+ subscribes: {},
120+ tasks: {},
121+ owned_coroutines: {},
122+ finished: {},
123+ }
124+
125+ ///|
126+ /// Spawn a coroutine owned by the current component-model async task.
127+ ///
128+ /// This is intended for runtime bridge work such as lowering local futures and
129+ /// streams to component-model handles. The coroutine does not participate in
130+ /// `TaskGroup` structured concurrency, but it is cancelled when the current
131+ /// component-model task is cancelled and keeps the waitable-set alive until it
132+ /// terminates.
133+ pub fn spawn_component_task_current(f : async () -> Unit ) -> Unit {
134+ let waitable_set = current_waitableset()
135+ guard ev.tasks.get(waitable_set) is Some (_)
136+ guard ev.owned_coroutines.get(waitable_set) is Some (owned)
137+ let coro = spawn(async fn() -> Unit {
138+ let coro = current_coroutine()
139+ defer owned.remove(coro)
140+ f()
141+ })
142+ owned.add(coro)
143+ }
108144
109145///|
110146pub fn detach_waitable(waitable_id : Int ) -> Unit {
@@ -125,7 +161,7 @@ pub async fn suspend_for_subtask(
125161
126162 // Helper: ensure cleanup is called once we've moved past Starting state
127163 fn ensure_cleanup(state : SubTaskState ) -> Unit {
128- if not( cleaned) && !(state is Starting ) {
164+ if ! cleaned && !(state is Starting ) {
129165 cleanup_after_started()
130166 cleaned = true
131167 }
@@ -159,7 +195,7 @@ pub async fn suspend_for_subtask(
159195 let set = @set.Set ::new()
160196 let subscriber = { event: None , coro: set }
161197 ev.subscribes.set(task.handle, subscriber)
162- for {
198+ for ;; {
163199 set.add(current_coroutine())
164200 waitable_join(task.handle, current_waitableset().0)
165201 defer subscriber.coro.remove(current_coroutine())
0 commit comments