Closed roflmuffin closed 6 months ago
Tested this with pretty jank code, and confirmed the output is as expected, 1024 calls per frame until the queue is emptied.
// Queue next world updates from as many threads as possible
var times = new Dictionary<float, int>();
var tasks = new List<Task>();
for (int i = 0; i < 10000; i++)
{
// Create a new task that will run on a random thread
tasks.Add(Task.Run(async () =>
{
await Server.NextWorldUpdate(() =>
{
// Track the server (main thread) time when the task is executed
times[Server.CurrentTime] = times.TryGetValue(Server.CurrentTime, out var time) ? time + 1 : 1;
});
}));
}
await Task.WhenAll(tasks);
Logger.LogInformation("{Times}", times);
{"14.25": 1024, "14.28125": 1024, "14.3125": 1024, "14.328125": 1024, "14.34375": 1024, "14.359375": 1024, "14.375": 1024, "14.40625": 1024, "14.421875": 1024, "14.4375": 784}
FunctionReference
identifiers, as these were not behind a lock, so multiple threads creating new function references (i.e.Server.NextFrame
) would cause id dictionary collisions and garbage collector would free the underlying callback, causing a crash.FunctionReference
created with a single use lifetime, previously if your lambda was re-used (and did not capture variables), it would be looked up in a dictionary and the same function callback would be used, causing misleading behaviour. Single use lambdas now always point to a fresh callback pointer.Closes #354