Description
Consider a Dart usage of a C api through ffi. A common pattern is the usage of context objects that must be explicitly setup and torn down for a given thread. ( This is simplified from an example program I wrote using https://sol.gfxile.net/soloud/ )
final lib = ffi.DynamicLibrary.open('some.dll');
class SoundSystem {
ffi.Pointer _audioContext = lib.createContext();
SoundSystem() {
lib.setupContext(_audioContext);
}
void playSound() {
lib.playSound(_audioContext);
}
void dispose() {
lib.destroyContext(_audioContext);
}
}
While this works well enough when performing a hot reload, but a hot restart may cause a native crash as recreating the SoundSystem with cause another audio context to be created without destroying the previous one.
During normal program execution this isn't a problem, since the dispose can be connected to the correct part of the application teardown logic ( or the handled by the OS as the program exits).
Trying to do something like teardown the entire widget tree may solve this problem, but would be expensive as well as a large behavior change with unintended consequences. Instead, I would propose adding a binding hook that allows developers/library authors to run callbacks before the hot restart is performed. The risk should be lower since the feature is opt-in, and failures could be ignored since the isolate would be destroyed anyway (though detecting these failures without using something like a timeout will be tricky).
For example, the above code could be modified like:
final lib = ffi.DynamicLibrary.open('some.dll');
class SoundSystem {
ffi.Pointer _audioContext = lib.createContext();
SoundSystem() {
if (kDebugMode) {
SomeBinding.instance!.addHotRestartTeardown(dispose);
}
lib.setupContext(_audioContext);
}
void playSound() {
lib.playSound(_audioContext);
}
void dispose() {
lib.destroyContext(_audioContext);
}
}
This won't work if the library is trying to be platform agnostic though - which means we might need to consider either dart level support or a convention around using a dart:developer API. For example, something like
import 'dart:developer';
final lib = ffi.DynamicLibrary.open('some.dll');
class SoundSystem {
ffi.Pointer _audioContext = lib.createContext();
SoundSystem() {
if (kDebugMode) {
registerExtension('dart.ext.hotRestartTeardown', dispose);
}
lib.setupContext(_audioContext);
}
void playSound() {
lib.playSound(_audioContext);
}
void dispose() {
lib.destroyContext(_audioContext);
}
}
Unfortunately I don't think that will work out of the box, since the various different libraries would stomp on eachother with different callbacks