Comment by modeless
3 days ago
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.
You basically need to wrap your program's `tick()` function. Otherwise you might be in the middle of malloc, hot-patch, and your struct's layout and alignment changes, and your program crashes due to undefined behavior.
The goal is that frameworks just bake `subsecond::current` into their `tick()` function and end-users get hot-patching for free.
How would you preempt the running program during malloc? Isn't there a well-defined reload point? Major red flags going up if your program can just change at any random point..
Also, didn't the article say explicitly that struct layout changes aren't supported??
There is a well-defined reload point—it’s the `subsecond::call` wrapper around `tick()`. But the hypothetical design that you seem to have in mind where this doesn’t exist would not have a well-defined reload point, so it would need to be able to preempt your program anywhere.
Layout changes are supported for structs that don’t persist across the well-defined reload point.
This is less worse than it sounds. The stuff I need hot reloading for is less than 5% of the total code base. It is usually stuff that I can't debug (ie: API response). So I am back and forth re-compiling. If I can get hot-reloading for that, that's a 95% time improvement for me. I could live with re-compiling for the rest of the code.
Strong agree here. The 'purity' BS of not modifying the running programs address space appears to come at the cost of significant programmer pain-in-the-ass. Having to hand-hold the library to maintain the indirection table is a hard no for me.
Metaprogramming that maintenance burden seems like it should be relatively straight-forward, if you've written a linker already.
The challenge is that if the program is busy in a spin loop, there's no way to preempt it and modify it. Things like malloc, spin loops, network requests, syscalls etc.
I looked into liveplusplus a lot and their unreal integration also requires a broker to get the most out of it. If you're building a game engine and want to support struct layout and alignment changes, you'll need to do some re-instancing. Hiding a `subsecond::call` deep in the bowels of the host framework hides it from the user and lets the framework handle any complicated state management it needs during hotpatches.
I wouldn't say it's purity - the first version of subsecond actually did do in-process modification - but after playing around with it for a while, I much preferred the light runtime integration. The dioxus integration was about 5 lines of code, so it's quite minimal.
FWIW I think the project is quite cool even if you “only” manage to roll out the subsecond integration into popular ecosystem runtimes.
Have you considered aiming at lower level dependencies that are even more ubiquitous? Like libc functions?
2 replies →