Files
RobustToolbox/Robust.Shared/Asynchronous/TaskManager.cs
83c2a1be11 [Dependency] source generator part 2 (#6550)
* [Dependency] source generator

No more reflection, no more codegen at runtime

Also various changes to Roslyn helpers to make this easier to write.

Requires all types with dependencies to be partial and not have readonly dependency fields. An analyzer enforces this at warning level, the previous injection strategies have remained in the code *for now* as a fallback.

No fallback is available for [field: Dependency] properties, due to a Roslyn bug.

Code Fixes exist. We love Roslyn

* Apply dependencies generator changes to all code

* Release notes

* Preprocessor got hands

* Handle nullable dependencies

These are bad but gotta deal with it.

* Apply suggestions from code review

Co-authored-by: Moony <moony@hellomouse.net>

* Fine, let's not use collection expressions

---------

Co-authored-by: Moony <moony@hellomouse.net>
2026-05-08 12:38:33 +02:00

106 lines
3.2 KiB
C#

using System;
using System.Threading;
using System.Threading.Tasks;
using Robust.Shared.Exceptions;
using Robust.Shared.IoC;
namespace Robust.Shared.Asynchronous
{
internal sealed partial class TaskManager : ITaskManager
{
private RobustSynchronizationContext _mainThreadContext = default!;
[Dependency] private IRuntimeLog _runtimeLog = default!;
public void Initialize()
{
_mainThreadContext = new RobustSynchronizationContext(_runtimeLog);
ResetSynchronizationContext();
}
public void ResetSynchronizationContext()
{
SynchronizationContext.SetSynchronizationContext(_mainThreadContext);
}
public void ProcessPendingTasks()
{
_mainThreadContext.ProcessPendingTasks();
}
public void RunOnMainThread(Action callback)
{
_mainThreadContext.Post(_runCallback, callback);
}
public void BlockWaitOnTask(Task task)
{
// NOTE: This code should be re-entry safe.
while (true)
{
var waitTask = _mainThreadContext.WaitOnPendingTasks().AsTask();
var idx = Task.WaitAny(task, waitTask);
if (idx == 0)
return;
_mainThreadContext.ProcessPendingTasks();
}
}
private static readonly SendOrPostCallback _runCallback = o => { ((Action?)o)?.Invoke(); };
}
[NotContentImplementable]
public interface ITaskManager
{
void Initialize();
void ProcessPendingTasks();
/// <summary>
/// Run a delegate on the main thread sometime later.
/// Thread safe.
/// </summary>
/// <remarks>
/// Useful if you want to run a callback from a separate thread.
/// </remarks>
/// <param name="callback">The callback that will be invoked on the main thread.</param>
void RunOnMainThread(Action callback);
/// <summary>
/// Synchronously wait for a main-thread task to complete.
/// This is effectively what you need to safely .Result a task on the main thread.
/// </summary>
/// <remarks>
/// Use of this method is only ever recommended in rare scenarios like shutdown. For most other scenarios you should really avoid blocking the main thread and use proper async instead.
/// </remarks>
void BlockWaitOnTask(Task task);
}
internal static class TaskManagerExt
{
/// <summary>
/// Run a callback on the main thread, returning a task that represents its completion.
/// </summary>
/// <seealso cref="ITaskManager.RunOnMainThread"/>
public static Task TaskOnMainThread(this ITaskManager taskManager, Action callback)
{
var tcs = new TaskCompletionSource();
taskManager.RunOnMainThread(() =>
{
try
{
callback();
tcs.SetResult();
}
catch (Exception e)
{
tcs.TrySetException(e);
}
});
return tcs.Task;
}
}
}