logoalt Hacker News

Subsecond: A runtime hotpatching engine for Rust hot-reloading

154 pointsby varbhatyesterday at 6:58 PM25 commentsview on HN

Comments

jkelleyrtpyesterday at 10:03 PM

Creator here - haven't had a chance to write up a blog post yet! Stay tuned.

The gist of it is that we intercept the Rust linking phase and then drive `rustc` manually. There's some diffing logic that compares assembly between compiles and then a linking phase where we patch symbols against the running process. Works across macOS, Windows, Linux, iOS, Android, and WASM. On my m4 I can get 130ms compile-patch times, quite wicked stuff.

We handle the hard parts that the traditional dylib-reloading doesn't including TLS, statics, constructors, etc.

I've been posting demos of it to our twitter page (yes twitter, sorry...)

- With bevy: https://x.com/dioxuslabs/status/1924762773734511035

- On iOS: https://x.com/dioxuslabs/status/1920184030173278608

- Frontend + backend (axum): https://x.com/dioxuslabs/status/1913353712552251860

- Ratatui (tui apps): https://x.com/dioxuslabs/status/1899539430173786505

Our unfinished release notes are here:

https://github.com/DioxusLabs/dioxus/releases/tag/v0.7.0-alp...

More details to come!

show 5 replies
eigenspacetoday at 7:48 AM

I would recommend looking into how julia handles code reloading with our builtin infrastructure and the Revise.jl package. Basically, every method to a function and as of v1.12 (currently in beta), every binding and struct definition has a "world-age" associated with it.

Basically, julia is dynamically typed, but inside a function it acts like a statically type language within a fixed world-age. So that means that based on the input types to a function, the compiler is able to know that the method table is not allowed to change, const-global, and types also can't change.

Between world-ages however, anything goes. You can redefine methods, redefine structs, etc. What's especailly nice is that old data doesn't become invalid, it's just living in an old world, and if you get a Foo struct you can know what world it comes from.

We have an invokelatest and invoke_in_world functions for advancing the world-age inside functions, and users creating something like an event loop just wrap their event loop iterations in an `invokelatest` and suddenly everything hot reloads automatically as you change code in your editor.

majketoday at 6:40 AM

About structs:

https://docs.rs/subsecond/0.7.0-alpha.1/subsecond/index.html...

  "In practice, frameworks that implement subsecond patching properly will throw out the old state and thus you should never witness a segfault due to misalignment or size changes. Frameworks are encouraged to aggressively dispose of old state that might cause size and alignment changes."
I don't think "throwing out the old state" is a sensible recommendation. "re-instancing" is called "update/upgrade/downgede of internal state" in OTP:

https://www.erlang.org/docs/24/man/gen_server#Module:code_ch...

Perhaps I'm missing something, maybe subsecond is a good tool for toy apps or for a developer workflow. But for anything serious, I'd think that managing layout of structs is a primary concern.

show 2 replies
modelessyesterday at 11:43 PM

Interesting, but the documentation makes it sound like you have to preemptively wrap all the code you think you might want to change in a special wrapper "call" function. If true that makes this a lot less appealing than existing solutions for other languages that can modify any function without special annotations.

show 3 replies
weinzierlyesterday at 10:42 PM

Very nice. For a long time I wondered who would use hotpatching but working with large Java applications made me appreciate the possibility even if it is not 100% reliable (as it is in Java).

From the docs Subsecond looks almost perfect. The only downside I found is that (if I understood correctly) you have to modify the function call in the source code of every function you want to hotpatch.

It is a bit mitigated in that the change does not cost anything in release builds, but it still is a big thing. Do I want sprinkle my code with call for every function I might potentially have to patch in a debugging session?

show 1 reply
cchancetoday at 3:56 AM

Wonder if the other web frameworks like leptos will adopt subsecond or adopt their own

mmastracyesterday at 8:49 PM

I'll have to give this a shot for some of the Rust server work I'm doing. progscrape.com uses a lot of tricks to boot quickly specifically because of the edit-compile-run cycle being slow (mostly deferred loading of indexes, etc).

My current day job @ Gel has me working on a Rust socket frontend for some pretty complex code and that could also be pretty interesting.

It seems to require that you choose a good "cutover" point in your codebase, but TBH that's probably not too hard to pick. The HTTP service handler in a webserver, the socket handlers in non-web serving code, etc.

It does appear to have a limitation where it will only allow the main crate to be hotpatched. That's less than ideal, but I suppose the convenience might justify some code structure changes to allow that.

written-beyondtoday at 2:24 AM

I really want rust dylibs to be a reality. A plugin system where a library can implement a specific versioned number of a trait and we can dynamically load in that implementation to get changed behaviour. Right now implementing anything like that requires a lot of unsafe which I'm not comfortable with.

prideoutyesterday at 10:43 PM

Neat but I would prefer simply using a dylib for the part of my code that I want to be reloadable.

show 1 reply
cyberaxtoday at 3:37 AM

Can we have the same for Go, pretty please?

show 1 reply