Skip to content

Commit d4f0885

Browse files
authored
remove CallbackCode::Poll (#1471)
* remove `CallbackCode::Poll` As of WebAssembly/component-model#578, that code no longer exists. Instead, we can emulate it via a combination of `CallbackCode::Yield` and `waitable-set.poll`. I'm working on a PR to make the corresponding changes to Wasmtime, and needed this to make the tests pass. * update C and Go generators to eliminate CALLBACK_CODE_POLL See my earlier commit updating the Rust generator for details. * fix test comment typos
1 parent 964dc8e commit d4f0885

File tree

4 files changed

+78
-63
lines changed

4 files changed

+78
-63
lines changed

crates/c/src/lib.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -729,7 +729,6 @@ typedef uint32_t {snake}_callback_code_t;
729729
#define {shouty}_CALLBACK_CODE_EXIT 0
730730
#define {shouty}_CALLBACK_CODE_YIELD 1
731731
#define {shouty}_CALLBACK_CODE_WAIT(set) (2 | (set << 4))
732-
#define {shouty}_CALLBACK_CODE_POLL(set) (3 | (set << 4))
733732
734733
typedef enum {snake}_event_code {{
735734
{shouty}_EVENT_NONE,

crates/go/src/wit_async.go

Lines changed: 67 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"fmt"
55
"runtime"
66
"unsafe"
7+
"wit_component/wit_runtime"
78
)
89

910
const EVENT_NONE uint32 = 0
@@ -20,7 +21,6 @@ const STATUS_RETURNED uint32 = 2
2021
const CALLBACK_CODE_EXIT uint32 = 0
2122
const CALLBACK_CODE_YIELD uint32 = 1
2223
const CALLBACK_CODE_WAIT uint32 = 2
23-
const CALLBACK_CODE_POLL uint32 = 3
2424

2525
const RETURN_CODE_BLOCKED uint32 = 0xFFFFFFFF
2626
const RETURN_CODE_COMPLETED uint32 = 0
@@ -74,37 +74,6 @@ func callback(event0, event1, event2 uint32) uint32 {
7474
yielding <- unit{}
7575
}
7676

77-
switch event0 {
78-
case EVENT_NONE:
79-
80-
case EVENT_SUBTASK:
81-
switch event2 {
82-
case STATUS_STARTING:
83-
panic(fmt.Sprintf("unexpected subtask status: %v", event2))
84-
85-
case STATUS_STARTED:
86-
87-
case STATUS_RETURNED:
88-
waitableJoin(event1, 0)
89-
subtaskDrop(event1)
90-
channel := state.pending[event1]
91-
delete(state.pending, event1)
92-
channel <- event2
93-
94-
default:
95-
panic("todo")
96-
}
97-
98-
case EVENT_STREAM_READ, EVENT_STREAM_WRITE, EVENT_FUTURE_READ, EVENT_FUTURE_WRITE:
99-
waitableJoin(event1, 0)
100-
channel := state.pending[event1]
101-
delete(state.pending, event1)
102-
channel <- event2
103-
104-
default:
105-
panic("todo")
106-
}
107-
10877
// Tell the Go scheduler to write to `state.channel` only after all
10978
// goroutines have either blocked or exited. This allows us to reliably
11079
// delay returning control to the host until there's truly nothing more
@@ -121,31 +90,75 @@ func callback(event0, event1, event2 uint32) uint32 {
12190
return false
12291
})
12392

124-
// Block this goroutine until the scheduler wakes us up.
125-
(<-state.channel)
93+
for {
94+
switch event0 {
95+
case EVENT_NONE:
12696

127-
if state.yielding != nil {
128-
contextSet(unsafe.Pointer(state))
129-
if len(state.pending) == 0 {
130-
return CALLBACK_CODE_YIELD
97+
case EVENT_SUBTASK:
98+
switch event2 {
99+
case STATUS_STARTING:
100+
panic(fmt.Sprintf("unexpected subtask status: %v", event2))
101+
102+
case STATUS_STARTED:
103+
104+
case STATUS_RETURNED:
105+
waitableJoin(event1, 0)
106+
subtaskDrop(event1)
107+
channel := state.pending[event1]
108+
delete(state.pending, event1)
109+
channel <- event2
110+
111+
default:
112+
panic("todo")
113+
}
114+
115+
case EVENT_STREAM_READ, EVENT_STREAM_WRITE, EVENT_FUTURE_READ, EVENT_FUTURE_WRITE:
116+
waitableJoin(event1, 0)
117+
channel := state.pending[event1]
118+
delete(state.pending, event1)
119+
channel <- event2
120+
121+
default:
122+
panic("todo")
123+
}
124+
125+
// Block this goroutine until the scheduler wakes us up.
126+
(<-state.channel)
127+
128+
if state.yielding != nil {
129+
contextSet(unsafe.Pointer(state))
130+
if len(state.pending) == 0 {
131+
return CALLBACK_CODE_YIELD
132+
} else {
133+
if state.waitableSet == 0 {
134+
panic("unreachable")
135+
}
136+
event0, event1, event2 = func() (uint32, uint32, uint32) {
137+
pinner := runtime.Pinner{}
138+
defer pinner.Unpin()
139+
buffer := wit_runtime.Allocate(&pinner, 8, 4)
140+
event0 := waitableSetPoll(state.waitableSet, buffer)
141+
return event0,
142+
unsafe.Slice((*uint32)(buffer), 2)[0],
143+
unsafe.Slice((*uint32)(buffer), 2)[1]
144+
}()
145+
if event0 == EVENT_NONE {
146+
return CALLBACK_CODE_YIELD
147+
}
148+
}
149+
} else if len(state.pending) == 0 {
150+
state.pinner.Unpin()
151+
if state.waitableSet != 0 {
152+
waitableSetDrop(state.waitableSet)
153+
}
154+
return CALLBACK_CODE_EXIT
131155
} else {
132156
if state.waitableSet == 0 {
133157
panic("unreachable")
134158
}
135-
return CALLBACK_CODE_POLL | (state.waitableSet << 4)
159+
contextSet(unsafe.Pointer(state))
160+
return CALLBACK_CODE_WAIT | (state.waitableSet << 4)
136161
}
137-
} else if len(state.pending) == 0 {
138-
state.pinner.Unpin()
139-
if state.waitableSet != 0 {
140-
waitableSetDrop(state.waitableSet)
141-
}
142-
return CALLBACK_CODE_EXIT
143-
} else {
144-
if state.waitableSet == 0 {
145-
panic("unreachable")
146-
}
147-
contextSet(unsafe.Pointer(state))
148-
return CALLBACK_CODE_WAIT | (state.waitableSet << 4)
149162
}
150163
}
151164

@@ -196,6 +209,9 @@ func Yield() {
196209
//go:wasmimport $root [waitable-set-new]
197210
func waitableSetNew() uint32
198211

212+
//go:wasmimport $root [waitable-set-poll]
213+
func waitableSetPoll(waitableSet uint32, eventPayload unsafe.Pointer) uint32
214+
199215
//go:wasmimport $root [waitable-set-drop]
200216
func waitableSetDrop(waitableSet uint32)
201217

crates/guest-rust/src/rt/async_support.rs

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -214,9 +214,9 @@ impl FutureState<'_> {
214214
// processing the future here anyway.
215215
me.cancel_inter_task_stream_read();
216216

217-
let mut context = Context::from_waker(&me.waker_clone);
218-
219217
loop {
218+
let mut context = Context::from_waker(&me.waker_clone);
219+
220220
// On each turn of this loop reset the state to "polling"
221221
// which clears out any pending wakeup if one was sent. This
222222
// in theory helps minimize wakeups from previous iterations
@@ -255,8 +255,12 @@ impl FutureState<'_> {
255255
assert!(!me.tasks.is_empty());
256256
if me.waker.sleep_state.load(Ordering::Relaxed) == SLEEP_STATE_WOKEN {
257257
if me.remaining_work() {
258-
let waitable = me.waitable_set.as_ref().unwrap().as_raw();
259-
break CallbackCode::Poll(waitable);
258+
let (event0, event1, event2) =
259+
me.waitable_set.as_ref().unwrap().poll();
260+
if event0 != EVENT_NONE {
261+
me.deliver_waitable_event(event1, event2);
262+
continue;
263+
}
260264
}
261265
break CallbackCode::Yield;
262266
}
@@ -415,7 +419,6 @@ enum CallbackCode {
415419
Exit,
416420
Yield,
417421
Wait(u32),
418-
Poll(u32),
419422
}
420423

421424
impl CallbackCode {
@@ -424,7 +427,6 @@ impl CallbackCode {
424427
CallbackCode::Exit => 0,
425428
CallbackCode::Yield => 1,
426429
CallbackCode::Wait(waitable) => 2 | (waitable << 4),
427-
CallbackCode::Poll(waitable) => 3 | (waitable << 4),
428430
}
429431
}
430432
}
@@ -546,9 +548,7 @@ pub fn block_on<T: 'static>(future: impl Future<Output = T>) -> T {
546548
drop(state);
547549
break result.unwrap();
548550
}
549-
CallbackCode::Yield | CallbackCode::Poll(_) => {
550-
event = state.waitable_set.as_ref().unwrap().poll()
551-
}
551+
CallbackCode::Yield => event = state.waitable_set.as_ref().unwrap().poll(),
552552
CallbackCode::Wait(_) => event = state.waitable_set.as_ref().unwrap().wait(),
553553
}
554554
}

tests/runtime-async/async/incomplete-writes/test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ func DroppedReaderTest(f1, f2 *FutureReader[*TestThing]) (*FutureReader[*TestThi
9191
thing := f2.Read()
9292

9393
// Write the thing to the first future, the read end of which
94-
// the calle4 will drop without reading from, forcing us to
94+
// the callee will drop without reading from, forcing us to
9595
// re-take ownership.
9696
assert(!tx1.Write(thing))
9797

@@ -116,7 +116,7 @@ func DroppedReaderLeaf(f1, f2 *FutureReader[*LeafThing]) (*FutureReader[*LeafThi
116116
thing := f2.Read()
117117

118118
// Write the thing to the first future, the read end of which
119-
// the calle4 will drop without reading from, forcing us to
119+
// the callee will drop without reading from, forcing us to
120120
// re-take ownership.
121121
assert(!tx1.Write(thing))
122122

0 commit comments

Comments
 (0)