Hot reloading C
Daniel Andersson
2019-05-12
In 2017 I found out about Handmade Hero, where Casey Muratori writes an entire game from scratch live on youtube streams. One interesting feature is that Casey wrote the game in major parts as a library. That made it possible to hot-reload the game. That is, he could keep the game running and make changes to the code. When he then recompiled the library the changes would take effect immediately. There was no need to restart the game. Pretty cool!
That pretty much makes it possible to have C similar to a dynamic- or scripting language. One of the main advantages, of not having to compile dynamic languages sources to run them goes away. Just using C with this ‘feature/trick’ might be enough to not also have to depend on Lua, or implementing your own Domain-Specific Language. There are drawbacks, or gotchas, with this method. For example, you cannot add or remove members of a struct between reloads.
Handmade Hero was written, initially at least, for Windows. I prefer coding on POSIX systems and back then I made a simple test project. foo is a library that has two functions to do work, return_text()
and do_work()
:
static const char* return_text()
{
return "Greger";
}
static int do_work(struct data_t* data)
{
if (data->counter < ARRAY_LEN) {
data->arr[data->counter++] = data->counter;
return 0;
} else {
// Bug out, print stuff just for show
print_data(data);
return -1;
}
}
When a reload is flagged, by using signal USR1, I call the function setCounter(15). This skips some work when reloading. The current libfoo is closed with dlclose(), next the new libfoo is opened with dlopen(), and then the library’s API of function pointers is obtained with dlsym(). Source code for sample. It is pretty cool to have C like a scripting language.
The video simulates a workflow, where code is written incrementally and recompiled, without restarting the program, to verify expected result.
The video shows playback of recorded input. The game has a server and a client. The way recording and playback work is both the server and client dump the current state to disk, when the recording is started. When the recording is stopped, the client’s input during recording is saved to disk. On each playback iteration, the state of server and client is read from disk and during playback the client will send the recorded actions at the recorded frame. Synchronizing start of playback is controlled by shared memory, so this will only work when the server and client are on the same computer. In the video, the code for the server is changed while the playback is happening, to show that iterating can be quick, whether it is changing something on the server side, or adjusting an animation or something else on the client side.
Another nice thing to have is the ability to watch for file changes, like a texture being changed and not having to restart the application. QOI is currently used instead of PNG for performance reasons.