Skip to content

Commit

Permalink
Merge pull request #19 from ronaldvanmanen/fixes/unmanaged_audio_devi…
Browse files Browse the repository at this point in the history
…ce_callback_is_garbage_collected

Fix: prevent unmanaged Audio Device callback from being garbage collected.
  • Loading branch information
ronaldvanmanen authored Sep 20, 2023
2 parents da98cff + cc4f070 commit 013a108
Showing 1 changed file with 14 additions and 10 deletions.
24 changes: 14 additions & 10 deletions sources/SDL2Sharp/AudioDevice.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
// 3. This notice may not be removed or altered from any source distribution.

using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using SDL2Sharp.Interop;

Expand All @@ -31,11 +30,13 @@ public sealed unsafe class AudioDevice : IDisposable

private AudioDeviceCallback _callback = null!;

private UnmanagedAudioDeviceCallback _unmanagedCallback = null!;

private SDL_AudioSpec _obtainedSpec;

private object _userdata = null!;

private GCHandle _handle = default;
private GCHandle _unmanagedUserdata = default;

private bool _disposed = false;

Expand Down Expand Up @@ -168,11 +169,11 @@ public bool TryOpen(AudioDeviceSpec spec, AudioDeviceCallback callback, object u
{
_callback = callback;
_userdata = userdata;
_handle = GCHandle.Alloc(this, GCHandleType.Normal);
_unmanagedUserdata = GCHandle.Alloc(this, GCHandleType.Normal);
_unmanagedCallback = new UnmanagedAudioDeviceCallback(HandleUnmanagedAudioDeviceCallback);

var audioDeviceCallback = Marshal.GetFunctionPointerForDelegate(UnmanagedAudioDeviceCallback);
desiredSpec.callback = audioDeviceCallback;
desiredSpec.userdata = (void*)(IntPtr)_handle;
desiredSpec.callback = Marshal.GetFunctionPointerForDelegate(_unmanagedCallback);
desiredSpec.userdata = (void*)(IntPtr)_unmanagedUserdata;
}

fixed (SDL_AudioSpec* obtainedSpec = &_obtainedSpec)
Expand All @@ -199,12 +200,15 @@ public void Close()
if (_deviceID != 0)
{
SDL.CloseAudioDevice(_deviceID);

_deviceID = 0;
_callback = null!;
_userdata = null!;
if (_handle.IsAllocated)
_unmanagedCallback = null!;

if (_unmanagedUserdata.IsAllocated)
{
_handle.Free();
_unmanagedUserdata.Free();
}
}
}
Expand Down Expand Up @@ -274,9 +278,9 @@ private void ThrowIfDisposed()
}

[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private delegate void UnmanagedAudioDeviceCallbackDelegate(void* userdata, byte* stream, int len);
private delegate void UnmanagedAudioDeviceCallback(void* userdata, byte* stream, int len);

private static void UnmanagedAudioDeviceCallback(void* userdata, byte* stream, int len)
private static void HandleUnmanagedAudioDeviceCallback(void* userdata, byte* stream, int len)
{
var audioDeviceHandle = GCHandle.FromIntPtr((IntPtr)userdata);
if (audioDeviceHandle.Target is AudioDevice audioDevice)
Expand Down

0 comments on commit 013a108

Please sign in to comment.