-
Notifications
You must be signed in to change notification settings - Fork 103
Description
UnityTaskProcessor::startTask has a one line implementation:
void UnityTaskProcessor::startTask(std::function<void()> f) {
System::Threading::Tasks::Task::Run(f);
}Task::Run takes a DotNet::System::Action instance, which is implicitly constructed from the std::function. The implicit constructor implementation looks like this:
void* (*Action::CreateDelegate)(void* pCallbackFunction) = nullptr;
Action::Action(std::function<FunctionSignature> callback) :
_handle(CreateDelegate(reinterpret_cast<void*>(new std::function<FunctionSignature>(std::move(callback)))))
{
}So a new std::function is allocated on the heap and then passed to CreateDelegate, which is a managed function. The managed function creates an instance of a managed class called ActionNativeFunction. ActionNativeFunction holds that std::function pointer (as an IntPtr). Its Invoke method calls back into native code to invoke the function. Finally, that Invoke is hooked up to a regular managed System.Action delegate.
End result: the C# System.Action "owns" a std::function that it can call at will. That System.Action can be passed to, in this case, Task.Run, which can now effectively call back into native code.
Here's the problem. When the managed ActionNativeFunction is finalized, the std::function will be deleted. This can happen even while the std::function is still running! I don't know how common this is, but I have a crash up on my screen right now where this is exactly what has happened (during finalization for AppDomain unload). This is #17 again, which was mostly fixed in #177, but apparently not for delegates.