using System; using System.Collections.Concurrent; using System.Runtime.InteropServices; using System.Threading.Tasks; using WhiteMagic.Hooking; namespace WhiteMagic.Execution; /// /// A crash-safe work queue drained on the target's own thread via a detour on a /// per-frame function. Callers queue work and receive the result (or exception) /// on their own thread through a completion handle. /// /// /// The pump assumes the frame function is parameterless and returns an . /// This matches common per-frame functions such as D3D9 EndScene. /// public sealed class MainThreadPump : IDisposable { private readonly DetourManager _detours; private readonly IntPtr _frameAddress; private readonly ConcurrentQueue _queue = new(); private Detour? _detour; private bool _installed; /// /// Creates a pump that will hook the frame function at . /// public MainThreadPump(DetourManager detours, IntPtr frameAddress) { _detours = detours; _frameAddress = frameAddress; } /// Returns after the frame hook has been applied. public bool IsInstalled => _installed; /// Installs the frame-function detour. public void Install() { if (_installed) return; _detour = _detours.Create("MainThreadPump", _frameAddress, (FrameDelegate)PumpHook); _detour.Apply(); _installed = true; } /// /// Queues work to run on the hooked thread and blocks until it completes. /// public TResult Execute(Func work) { if (!_installed) { throw new InvalidOperationException( "The main-thread pump is not installed. Call Install() first."); } var tcs = new TaskCompletionSource(); _queue.Enqueue(new WorkItem(() => work()!, tcs)); object? result = tcs.Task.GetAwaiter().GetResult(); return (TResult)result!; } /// /// Queues work to run on the hooked thread and returns a . /// public Task ExecuteAsync(Func work) { if (!_installed) { throw new InvalidOperationException( "The main-thread pump is not installed. Call Install() first."); } var tcs = new TaskCompletionSource(); object? Box() => work()!; _queue.Enqueue(new WorkItem(Box, r => tcs.SetResult((TResult)r!), ex => tcs.SetException(ex))); return tcs.Task; } /// Removes the frame-function detour if it is installed. public void Dispose() { if (_installed && _detour is not null) { _detour.Remove(); _installed = false; } } private int PumpHook() { while (_queue.TryDequeue(out WorkItem? item)) { try { object? result = item.Work(); item.SetResult(result); } catch (Exception ex) { item.SetException(ex); } } // Call the original frame function so rendering/game logic continues. return _detour is null ? 0 : (int?)_detour.CallOriginal() ?? 0; } [UnmanagedFunctionPointer(CallingConvention.Cdecl)] private delegate int FrameDelegate(); private sealed class WorkItem { private readonly Action? _setResult; private readonly Action? _setException; public WorkItem(Func work, Action setResult, Action setException) { Work = work; _setResult = setResult; _setException = setException; } public WorkItem(Func work, TaskCompletionSource tcs) { Work = work; _setResult = r => tcs.SetResult(r); _setException = ex => tcs.SetException(ex); } public Func Work { get; } public void SetResult(object? result) { _setResult?.Invoke(result); } public void SetException(Exception exception) { _setException?.Invoke(exception); } } }