i am writing a Unity script which serves as a JACK client. For this, I need to register a callback function, in which the audio signal gets processed. Since I use managed C# code, I create a delegate and retrieve the function pointer from it, which I pass to the jack_set_process_callback function. Since I found out, that the delegate will be garbage collected after some time, which would cause a access violation and a crash after some time, I now keep a reference as field. However, the application still crashes when I stop the Unity player. Here is the code:
using System;
using UnityEngine;
using System.Runtime.InteropServices;
public class SimpleJackClient : MonoBehaviour
{
[DllImport("libjack64.dll")]
private static extern IntPtr jack_client_open(string clientName, int options, out int status);
[DllImport("libjack64.dll")]
private static extern IntPtr jack_client_close(IntPtr jackClient);
[DllImport("libjack64.dll")]
private static extern int jack_activate(IntPtr client);
[DllImport("libjack64.dll")]
private static extern int jack_deactivate(IntPtr client);
[DllImport("libjack64.dll")]
private static extern int jack_set_process_callback(IntPtr client, IntPtr callback, IntPtr arg);
public delegate int CallbackDelegate(int samplesPerFrame, IntPtr arg);
// Hold a reference to the process callback to avoid it to be garbage collected
private CallbackDelegate callbackDelegate;
public string clientName = "UnityClient";
private IntPtr jackClientPtr;
private void Start()
{
int status;
jackClientPtr = jack_client_open(clientName, 0, out status);
callbackDelegate = new CallbackDelegate(Process);
IntPtr processCallbackPtr = Marshal.GetFunctionPointerForDelegate(callbackDelegate);
jack_set_process_callback(jackClientPtr, processCallbackPtr, IntPtr.Zero);
jack_activate(jackClientPtr);
}
public int Process(int samplesPerFrame, IntPtr arg)
{
return 0;
}
private void OnDestroy()
{
if (jackClientPtr != IntPtr.Zero)
{
jack_deactivate(jackClientPtr);
jack_client_close(jackClientPtr);
}
}
}
I get an error message: "Fatal error in GC: Suspend thread loop failed."
This is the crash stack trace:
0x00007FFAB3884F2F (mono-2.0-bdwgc) [C:\build\output\Unity-Technologies\mono\external\bdwgc\win32_threads.c:1248] GC_suspend
0x00007FFAB388466B (mono-2.0-bdwgc) [C:\build\output\Unity-Technologies\mono\external\bdwgc\win32_threads.c:1324] GC_stop_world
0x00007FFAB38849EB (mono-2.0-bdwgc) [C:\build\output\Unity-Technologies\mono\external\bdwgc\alloc.c:782] GC_stopped_mark
0x00007FFAB3885558 (mono-2.0-bdwgc) [C:\build\output\Unity-Technologies\mono\external\bdwgc\alloc.c:580] GC_try_to_collect_inner
0x00007FFAB38763E5 (mono-2.0-bdwgc) [C:\build\output\Unity-Technologies\mono\external\bdwgc\alloc.c:1224] GC_gcollect
0x00007FF6C2D68B5D (Unity) CleanupAfterLoad
0x00007FF6C2DBC50C (Unity) LoadSceneOperation::CompletePreloadManagerLoadSceneEditor
0x00007FF6C2DBD09B (Unity) LoadSceneOperation::IntegrateMainThread
0x00007FF6C2DC0C91 (Unity) PreloadManager::UpdatePreloadingSingleStep
0x00007FF6C2DC126F (Unity) PreloadManager::WaitForAllAsyncOperationsToComplete
0x00007FF6C3FD106C (Unity) EditorSceneManager::RestoreSceneBackups
0x00007FF6C3CD8FED (Unity) PlayerLoopController::ExitPlayMode
0x00007FF6C3CE8AE1 (Unity) PlayerLoopController::SetIsPlaying
0x00007FF6C3CEBF4B (Unity) Application::TickTimer
0x00007FF6C4140FBA (Unity) MainMessageLoop
0x00007FF6C414588B (Unity) WinMain
0x00007FF6C552315E (Unity) __scrt_common_main_seh
0x00007FFB5A4A26AD (KERNEL32) BaseThreadInitThunk
0x00007FFB5AEAA9F8 (ntdll) RtlUserThreadStart
When I set a breakpoint in OnDestroy(), and then continue, the crash doesn't occur anymore. Do you have an idea how this crash is caused? Is the delegate perhaps still destroyed too early?