Skip to content

Document the new waitqueue design#110

Merged
tlively merged 3 commits into
mainfrom
new-waitqueue-design
Apr 28, 2026
Merged

Document the new waitqueue design#110
tlively merged 3 commits into
mainfrom
new-waitqueue-design

Conversation

@tlively
Copy link
Copy Markdown
Member

@tlively tlively commented Apr 9, 2026

Specify the new types and instructions for a waitqueue design that
decouples the waitqueue itself from the control word. This is both
more geenral and simpler to implement than the previous waitqueue
design.

Closes #102.

Specify the new types and instructions for a waitqueue design that
decouples the waitqueue itself from the control word. This is both
more geenral and simpler to implement than the previous waitqueue
design.

Closes #102.
@tlively
Copy link
Copy Markdown
Member Author

tlively commented Apr 9, 2026

Also cc @manoskouk.

Copy link
Copy Markdown
Member

@rossberg rossberg left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks right, except for table and memory types.

I'm not too fond of how this overloads the different equality operators. That is quite at odds with having i32.eq, i64.eq, and ref.eq as separate instructions, as well as with the two memory.waitN instructions. I understand that this saves a few opcodes, but wouldn't it be more consistent if we had separate wait32, wait64, and wait_ref variants for all?

```

Like their linear memory counterparts, the `wait` operations take an expected value and a
relative timeout in nanoseconds (where negative arguments are interpreted as meaning infinite timeout).
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
relative timeout in nanoseconds (where negative arguments are interpreted as meaning infinite timeout).
relative timeout in nanoseconds (interpreted as signed, where negative arguments are interpreted as meaning infinite timeout).

Comment on lines +506 to +508
C |- table.wait x : [(ref null (shared waitqueue)) i32 t i64] -> [i32]
-- C.tables[x] = shared? table t'
-- t = wait_expected(t')
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
C |- table.wait x : [(ref null (shared waitqueue)) i32 t i64] -> [i32]
-- C.tables[x] = shared? table t'
-- t = wait_expected(t')
C |- table.wait x : [(ref null (shared waitqueue)) at t i64] -> [i32]
-- C.tables[x] = shared? table at lim t'
-- t = wait_expected(t')

Comment on lines +510 to +514
C |- memory.wait32 x : [(ref null (shared waitqueue)) t i32 i64] -> [i32]
-- C.memories[x] = shared? memory t

C |- memory.wait64 x : [(ref null (shared waitqueue)) t i64 i64] -> [i32]
-- C.memories[x] = shared? memory t
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
C |- memory.wait32 x : [(ref null (shared waitqueue)) t i32 i64] -> [i32]
-- C.memories[x] = shared? memory t
C |- memory.wait64 x : [(ref null (shared waitqueue)) t i64 i64] -> [i32]
-- C.memories[x] = shared? memory t
C |- memory.wait32 x : [(ref null (shared waitqueue)) at i32 i64] -> [i32]
-- C.memories[x] = shared? memory at lim
C |- memory.wait64 x : [(ref null (shared waitqueue)) at i64 i64] -> [i32]
-- C.memories[x] = shared? memory at lim

@tlively
Copy link
Copy Markdown
Member Author

tlively commented Apr 9, 2026

I'm not too fond of how this overloads the different equality operators. That is quite at odds with having i32.eq, i64.eq, and ref.eq as separate instructions, as well as with the two memory.waitN instructions. I understand that this saves a few opcodes, but wouldn't it be more consistent if we had separate wait32, wait64, and wait_ref variants for all?

It's rather meant to be consistent with things like struct.get or table.get, which work for any field or element type. It's also consistent with the long-standing documented design for {struct,array,global}.atomic.rmw.cmpxchg, which also works for i32, i64, and eqref elements.

We could of course just add more instructions if we really wanted to, but I don't see any benefit.

@rossberg
Copy link
Copy Markdown
Member

rossberg commented Apr 9, 2026

Well, get and set only perform "parametric" reads/writes, they don't inspect or operate on the values themselves (only the _s/u variants do, and they are fixed to a single type). But I agree that cmpxchg is in the same class.

We used to have "no overloading" as one of our slogans. That informed a lot of Wasm's design. I'm not sure what to think about dropping that in passing without a wider discussion, and some agreement about where to draw the line in the future.

@tlively
Copy link
Copy Markdown
Member Author

tlively commented Apr 9, 2026

Fair enough. I'll add a note to this PR for now and add something to the next CG agenda.

@tlively
Copy link
Copy Markdown
Member Author

tlively commented Apr 23, 2026

The overloading issue is being tracked in #114. @rossberg, would you object to landing this overview update while we resolve that question separately?

@rossberg
Copy link
Copy Markdown
Member

No, that's fine.

Copy link
Copy Markdown
Contributor

@jakobkummerow jakobkummerow left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not a huge fan because I see the "extra flexibility" of this design as unnecessary complexity, but I'm not going to block this if it's what y'all want.

Regarding the "no overloading" question: IMHO the type immediate is enough that I wouldn't consider this overloading. Comparing with Wasm 1.0's arithmetic instructions (such as i32.add vs i64.add), they had no type immediate, so their design could be viewed as "baking in" the type immediate into the one-byte encoding. In a hypothetical alternative where arithmetic ops do have a type immediate, surely there'd only be a single add $t (with $t being one of i32, i64, f32 etc), and nobody would demand having i32.add $t that's invalid if $t != i32 and separately i64.add $t that's invalid if $t != i64 etc. So by analogy, having struct.wait32 $struct $field that's invalid if C.types[$struct][$field] != i32 is likewise a superfluous duplication of the type annotation. The "principal types" property is maintained with just one copy of the type information.

@Liedtke
Copy link
Copy Markdown

Liedtke commented Apr 23, 2026

[...] they had no type immediate, so their design could be viewed as "baking in" the type immediate into the one-byte encoding.

In this particular case the initial instruction already uses a multi-byte sequence for encoding the instruction.
So basically we have 2 Bytes instruction + 1 Byte type immedate (ignoring other immedates here for simplicity).
If we move the type immediate into the instruction, then the main difference is that we move the 1 Byte type immedate into the instruction either by making the current instruction be a prefix and the type immediate being the last byte of the multi-byte instruction or by merging them into 2 Bytes.
The first one really just changes the first bit in the LEB encoding but I otherwise have difficulties seeing a difference in semantics. The second option certainly has some advantages there but the disadvantages is that it will be an instruction-specific matching from instruction code to type instead of the type immediate that is shared by many instructions?

So from a naive perspective, this feels like the differences here are really just in the actual encoding (and the text representation) but this doesn't really affect the semantics of Wasm?

@rossberg
Copy link
Copy Markdown
Member

@jakobkummerow, @Liedtke, I suggest to continue the discussion on issue #114 that @tlively created.

@tlively tlively merged commit 4f73b3e into main Apr 28, 2026
@tlively tlively deleted the new-waitqueue-design branch April 28, 2026 18:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Waitqueue as a packed type

5 participants