Skip to content

Modular, multi-threaded, transitioning plugins#5779

Merged
laurmaedje merged 2 commits intomainfrom
plugin-rework
Jan 30, 2025
Merged

Modular, multi-threaded, transitioning plugins#5779
laurmaedje merged 2 commits intomainfrom
plugin-rework

Conversation

@laurmaedje
Copy link
Member

This PR makes a number of changes to the plugin system.

Modular

The plugin function now returns a normal module containing Typst functions instead of a dedicated plugin type. As a result, plugin functions interact much better with the rest of Typst: There is no special method call handling for plugins anymore, plugin function can now be accessed and stored without being called, you can use .with on then and you can import from plugins.

#import plugin("/assets/plugins/hello.wasm"): hello, double_it

Multi-threaded

Plugins can now run multi-threaded. Typst will automatically do this behind the scenes with no involvement of the plugin. Plugins needn't be thread-safe. Rather, Typst will spin up multiple instance of the plugin as needed. For documents relying on expensive plugin calls (like diagramming) this can provide a big speed-up.

Flamegraph of multi-threaded plugin execution

Transitioning

A new plugin.transition API is introduced which allows plugins to run impure initialization (closes #3313) in a way that doesn't break Typst's purity guarantees.

This API comes alongside multi-threading for a simple reason: They are a few existing plugins which (wrongfully :)) rely on side effects being observable. With multiple WebAssembly instances being spun up due to multi-threading, these plugins will break.

Instead of calling their mutable functions directly, such plugins should use the transition API. This API runs a mutable function call and then returns a new, derived module whose functions will observe the side effects.

Here's an example: We call add via the transition API. The call
mutated.get() on the derived module will observe the addition. Meanwhile, the
original module remains untouched as demonstrated by the base.get() call.

#let base = plugin("hello-mut.wasm")
#assert.eq(base.get(), "[]")

#let mutated = plugin.transition(base.add, "hello")
#assert.eq(base.get(), "[]")
#assert.eq(mutated.get(), "[hello]")

After transitioning a plugin, Typst will take a memory snapshot and restore that snapshot for any fresh WebAssembly instance created due to multi-threading.

Calling impure plugin functions without the transition API will still yield unpredictable results. I've considered enforcing purity by resetting the memory after every individual call, but I think the cost of doing this is prohibitive for plugins with many cheap calls. An alternative would be resetting the memory after a larger number of wasm "fuel" steps instead of after every call. That would expose a few more bad mutable calls, nudging plugin developers into the right direction. However, multi-threading by itself will also do that to an extent, so I'm not yet sure where it's really worth it, so it's left as potential future work.

@lublak
Copy link

lublak commented Jan 30, 2025

@laurmaedje because it's a breaking change (which is fine). Would be a good idea to implement an additional compatible with field in the typst.toml.
compatible_with = ">=0.13"
compatible_with = "0.13,0.14".
Jus for the feature :)

@laurmaedje
Copy link
Member Author

The compiler field already says compatible_with >= X and compatible_with < X doesn't really make sense as you'd have to edit it after the fact.

The module change is definitely breaking, but the purity requirement was actually there from the start in the docs, we just haven't made use of it yet, so that's technically not breaking. :)

@lublak
Copy link

lublak commented Jan 30, 2025

@laurmaedje Oh, I had overlooked that. Of course the compiler field is sufficient 😄

@laurmaedje
Copy link
Member Author

I looked into it and for correctness, technically, we would also need to snapshot WASM globals, not just the memory, in plugin.transition. However, wasmi does not seem to expose an API to access non-exported globals. I opened a feature request on the wasmtime repository. (Wasmi mostly seems to mirror wasmtime's API so proposing it there seemed more fruitful.)

In the meantime, I would proceed with simply not snapshotting the globals. LLVM-based compilers apparently only emit a single global for a shadow stack pointer, which should always be the same after the call stack is drained. (At least I hope so.) And pretty much all plugins are written with LLVM-based compilers.

I added a disclaimer to the transition docs.

@laurmaedje laurmaedje added this pull request to the merge queue Jan 30, 2025
Merged via the queue into main with commit be1fa91 Jan 30, 2025
12 checks passed
@laurmaedje laurmaedje deleted the plugin-rework branch January 30, 2025 13:45
stelzo pushed a commit to stelzo/typst that referenced this pull request Nov 21, 2025
git download method

fixed warinings

documentation

cli updater adaptation and clippy fixes

enhanced documentation

add git downloader default impl

migrating from git2 to gitoxide crate for git downloads

Add support for `c2sc` OpenType feature in `smallcaps` (typst#5655)

Just add MathText SyntaxKind

Basic SymbolElem addition

Use SymbolElem in more places and add `char` cast for content

Add SymbolElem to realization

Update math TextElem layout to separate out SymbolElem

Handle boxes and blocks a bit better in HTML export (typst#5744)

Co-authored-by: Martin Haug <[email protected]>

Tweak HTML pretty printing (typst#5745)

Semantic paragraphs (typst#5746)

Fix space collapsing for explicit paragraphs (typst#5749)

Support first-line-indent for every paragraph (typst#5768)

Fixed typo in the new outline docs (typst#5772)

Resolve bound name of bare import statically (typst#5773)

Fix typo in scripting.md (typst#5783)

Modular, multi-threaded, transitioning plugins (typst#5779)

Include images from raw pixmaps and more (typst#5632)

Co-authored-by: PgBiel <[email protected]>
Co-authored-by: Laurenz <[email protected]>

Change type repr to short name (typst#5788)

Disable cjk_latin_spacing in raw by default (typst#5753)

Change the default math class of U+22A5 ⊥ UP TACK to Normal (typst#5714)

Revert adding `flatten-text` to `image` (typst#5789)

Refactor `Scope` (typst#5797)

Enable HTML feature in docs generator (typst#5800)

Scope deprecations (typst#5798)

Fix typo in page documentation (typst#5804)

Bump openssl from 0.10.66 to 0.10.70 (typst#5802)

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

Bump codex to 0.1.0 (typst#5805)

Bump dependencies (typst#5808)

Fix small copy-paste oversight (typst#5811)

Bump more dependencies (typst#5813)

Export target docs (typst#5812)

Co-authored-by: Martin Haug <[email protected]>

0.13 changelog (typst#5801)

Release Candidate 1

Autocomplete content methods (typst#5822)

Documentation fixes and improvements (typst#5816)

Fix docs outline for nested definitions (typst#5823)

Document removals in changelog (typst#5827)

Fix unnecessary import rename warning (typst#5828)

Don't crash on image with zero DPI (typst#5835)

Add warning for `pdf.embed` elem used with HTML (typst#5829)

Add smart quotes for Bulgarian (typst#5807)

Respect `par` constructor arguments (typst#5842)

Bump `typst-assets`

Fix autocomplete and jumps in math (typst#5849)

Update documentation for `float.{to-bits, from-bits}` (typst#5836)

`Gradient::repeat`: Fix floating-point error in stop calculation (typst#5837)

Lazy parsing of the package index (typst#5851)

Remove Linux Libertine warning (typst#5876)

Bring back type/str compatibility for 0.13, with warnings and hints (typst#5877)

More robust SVG auto-detection (typst#5878)

HTML export: Use `<code>` for inline `RawElem` (typst#5884)

--make-deps fixes (typst#5873)

Update changelog (typst#5894)

Version bump

Fix HTML export of table with gutter (typst#5920)

Fix comparison of `Func` and `NativeFuncData` (typst#5943)

HTML export: fix elem counting on classify_output (typst#5910)

Co-authored-by: Laurenz <[email protected]>

Fix introspection of HTML root sibling metadata (typst#5953)

Fix high CPU usage due to inotify watch triggering itself (typst#5905)

Co-authored-by: Laurenz <[email protected]>

Fix false positive for type/str comparison warning (typst#5957)

Fix paper name in page setup guide (typst#5956)

Fix curve with multiple non-closed components. (typst#5963)

Fix docs example with type/string comparison (typst#5987)

Correct typo (typst#5971)

Make `array.chunks` example more readable (typst#5975)

Hotfix for labels on symbols (typst#6015)

Replace `par` function call in tutorial (typst#6023)

Mention that `sym.ohm` was removed in the 0.13.0 changelog (typst#6017)

Co-authored-by: Laurenz <[email protected]>

Mark breaking symbol changes as breaking in 0.13.0 changelog (typst#6024)

0.13.1 changelog (typst#6025)

Version bump

dep min 1.81

use 1.81 in ci
stelzo pushed a commit to stelzo/typst that referenced this pull request Nov 21, 2025
git download method

fixed warinings

documentation

cli updater adaptation and clippy fixes

enhanced documentation

add git downloader default impl

migrating from git2 to gitoxide crate for git downloads

Add support for `c2sc` OpenType feature in `smallcaps` (typst#5655)

Just add MathText SyntaxKind

Basic SymbolElem addition

Use SymbolElem in more places and add `char` cast for content

Add SymbolElem to realization

Update math TextElem layout to separate out SymbolElem

Handle boxes and blocks a bit better in HTML export (typst#5744)

Co-authored-by: Martin Haug <[email protected]>

Tweak HTML pretty printing (typst#5745)

Semantic paragraphs (typst#5746)

Fix space collapsing for explicit paragraphs (typst#5749)

Support first-line-indent for every paragraph (typst#5768)

Fixed typo in the new outline docs (typst#5772)

Resolve bound name of bare import statically (typst#5773)

Fix typo in scripting.md (typst#5783)

Modular, multi-threaded, transitioning plugins (typst#5779)

Include images from raw pixmaps and more (typst#5632)

Co-authored-by: PgBiel <[email protected]>
Co-authored-by: Laurenz <[email protected]>

Change type repr to short name (typst#5788)

Disable cjk_latin_spacing in raw by default (typst#5753)

Change the default math class of U+22A5 ⊥ UP TACK to Normal (typst#5714)

Revert adding `flatten-text` to `image` (typst#5789)

Refactor `Scope` (typst#5797)

Enable HTML feature in docs generator (typst#5800)

Scope deprecations (typst#5798)

Fix typo in page documentation (typst#5804)

Bump openssl from 0.10.66 to 0.10.70 (typst#5802)

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

Bump codex to 0.1.0 (typst#5805)

Bump dependencies (typst#5808)

Fix small copy-paste oversight (typst#5811)

Bump more dependencies (typst#5813)

Export target docs (typst#5812)

Co-authored-by: Martin Haug <[email protected]>

0.13 changelog (typst#5801)

Release Candidate 1

Autocomplete content methods (typst#5822)

Documentation fixes and improvements (typst#5816)

Fix docs outline for nested definitions (typst#5823)

Document removals in changelog (typst#5827)

Fix unnecessary import rename warning (typst#5828)

Don't crash on image with zero DPI (typst#5835)

Add warning for `pdf.embed` elem used with HTML (typst#5829)

Add smart quotes for Bulgarian (typst#5807)

Respect `par` constructor arguments (typst#5842)

Bump `typst-assets`

Fix autocomplete and jumps in math (typst#5849)

Update documentation for `float.{to-bits, from-bits}` (typst#5836)

`Gradient::repeat`: Fix floating-point error in stop calculation (typst#5837)

Lazy parsing of the package index (typst#5851)

Remove Linux Libertine warning (typst#5876)

Bring back type/str compatibility for 0.13, with warnings and hints (typst#5877)

More robust SVG auto-detection (typst#5878)

HTML export: Use `<code>` for inline `RawElem` (typst#5884)

--make-deps fixes (typst#5873)

Update changelog (typst#5894)

Version bump

Fix HTML export of table with gutter (typst#5920)

Fix comparison of `Func` and `NativeFuncData` (typst#5943)

HTML export: fix elem counting on classify_output (typst#5910)

Co-authored-by: Laurenz <[email protected]>

Fix introspection of HTML root sibling metadata (typst#5953)

Fix high CPU usage due to inotify watch triggering itself (typst#5905)

Co-authored-by: Laurenz <[email protected]>

Fix false positive for type/str comparison warning (typst#5957)

Fix paper name in page setup guide (typst#5956)

Fix curve with multiple non-closed components. (typst#5963)

Fix docs example with type/string comparison (typst#5987)

Correct typo (typst#5971)

Make `array.chunks` example more readable (typst#5975)

Hotfix for labels on symbols (typst#6015)

Replace `par` function call in tutorial (typst#6023)

Mention that `sym.ohm` was removed in the 0.13.0 changelog (typst#6017)

Co-authored-by: Laurenz <[email protected]>

Mark breaking symbol changes as breaking in 0.13.0 changelog (typst#6024)

0.13.1 changelog (typst#6025)

Version bump

dep min 1.81

use 1.81 in ci
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.

Enable Wasm plugin initialization or Wasm plugin state transistion.

2 participants