This commit is contained in:
watrabi
2025-09-18 17:55:52 -04:00
commit 977f1ff4b8
15030 changed files with 17324420 additions and 0 deletions
@@ -0,0 +1,149 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading;
using Microsoft.Ccr.Core;
using Roblox.Common.Properties;
namespace Roblox.Ccr;
public class DispatcherMonitor : IDisposable
{
private readonly Dispatcher dispatcher;
private readonly ICollection<Thread> threads = new HashSet<Thread>();
private Thread thread;
public DispatcherMonitor(Dispatcher dispatcher)
{
this.dispatcher = dispatcher;
GatherWorkerThreads();
dispatcher.UnhandledException += dispatcher_UnhandledException;
thread = new Thread(Monitor);
thread.IsBackground = true;
thread.Name = "DispatcherMonitor: " + dispatcher.Name;
thread.Start();
}
private void dispatcher_UnhandledException(object sender, UnhandledExceptionEventArgs e)
{
ExceptionHandler.LogException(new ApplicationException($"{dispatcher.Name} had an unhandled exception", e.ExceptionObject as Exception));
}
private void Monitor()
{
int count = 0;
TimeSpan sleep = TimeSpan.Zero;
while (true)
{
try
{
int pendingTaskCount = dispatcher.PendingTaskCount;
int trigger = Settings.Default.CcrServiceBacklogTrigger;
if (trigger <= 0)
{
sleep = TimeSpan.FromSeconds(10.0);
}
else if (pendingTaskCount > trigger)
{
count++;
if (count >= 4)
{
ReportBacklog(pendingTaskCount);
sleep = TimeSpan.FromMinutes(1.0);
}
}
else
{
count = 0;
}
}
catch (ThreadAbortException)
{
break;
}
catch (Exception ex)
{
ExceptionHandler.LogException(ex);
}
if (sleep != TimeSpan.Zero)
{
Thread.Sleep(sleep);
sleep = TimeSpan.Zero;
continue;
}
TimeSpan wait = Settings.Default.CcrServiceBacklogTriggerInterval;
if (wait != TimeSpan.Zero)
{
Thread.Sleep((int)(wait.TotalMilliseconds / 4.0));
}
else
{
Thread.Sleep(TimeSpan.FromSeconds(10.0));
}
}
}
private void GatherWorkerThreads()
{
int count = 2 * dispatcher.WorkerThreadCount;
for (int i = 0; i < 2 * dispatcher.WorkerThreadCount; i++)
{
dispatcher.DispatcherQueues[0].Enqueue(Arbiter.FromHandler(delegate
{
Thread.Sleep(200);
lock (threads)
{
threads.Add(Thread.CurrentThread);
}
Interlocked.Decrement(ref count);
}));
}
while (count > 0)
{
Thread.Sleep(10);
}
}
private IEnumerable<StackTrace> GetWorkerStacks()
{
foreach (Thread t in threads)
{
t.Suspend();
StackTrace trace;
try
{
trace = new StackTrace(t, needFileInfo: true);
}
catch (Exception)
{
continue;
}
finally
{
t.Resume();
}
yield return trace;
}
}
private void ReportBacklog(int pendingTasks)
{
string message = $"CcrService detected a backlog of {pendingTasks}. These are the currently running tasks:\r\n\r\n";
foreach (StackTrace trace in GetWorkerStacks())
{
message = message + trace.ToString() + "\r\n\r\n";
}
ExceptionHandler.LogException(message, EventLogEntryType.Warning, 4061);
}
public void Dispose()
{
if (dispatcher != null)
{
dispatcher.Dispose();
}
threads.Clear();
}
}
@@ -0,0 +1,26 @@
using System;
using Microsoft.Ccr.Core;
namespace Roblox.Ccr;
public class ExceptionPort<T> : PortSet<T, Exception>
{
public static implicit operator T(ExceptionPort<T> port)
{
Exception ex = (Exception)port.P1.Test();
if (ex != null)
{
throw ex;
}
return (T)port.P0.Test();
}
public void Check()
{
Exception ex = (Exception)base.P1.Test();
if (ex != null)
{
throw ex;
}
}
}
@@ -0,0 +1,123 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading;
using System.Web;
using Microsoft.Ccr.Core;
using Roblox.Common;
namespace Roblox.Ccr;
public abstract class HttpHandler : IHttpAsyncHandler, IHttpHandler
{
private static long _PendingAsyncCalls;
public abstract bool IsReusable { get; }
static HttpHandler()
{
Thread thread = new Thread(MonitorPerformance);
thread.IsBackground = true;
thread.Name = "Performance Monitor: Ccr.HttpHandler";
thread.Start();
}
protected abstract IEnumerator<ITask> Execute(HttpContext context);
protected virtual bool SynchronousExecute(HttpContext context)
{
return false;
}
private IEnumerator<ITask> ExecuteAndComplete(HttpContext context, FastAsyncResult result)
{
IEnumerator<ITask> enu;
try
{
enu = Execute(context);
}
catch (Exception completed2)
{
result.SetCompleted(completed2);
yield break;
}
using (enu)
{
while (true)
{
try
{
if (!enu.MoveNext())
{
result.SetCompleted();
break;
}
}
catch (Exception completed)
{
result.SetCompleted(completed);
break;
}
yield return enu.Current;
}
}
}
private static void MonitorPerformance()
{
try
{
string categoryName = "Roblox Ccr.HttpHandler";
if (!PerformanceCounterCategory.Exists(categoryName))
{
CounterCreationDataCollection counterCreationDataCollection = new CounterCreationDataCollection();
counterCreationDataCollection.Add(new CounterCreationData("Pending Async Calls", string.Empty, PerformanceCounterType.NumberOfItems64));
PerformanceCounterCategory.Create(categoryName, string.Empty, PerformanceCounterCategoryType.SingleInstance, counterCreationDataCollection);
}
PerformanceCounter perfPendingAsyncCalls = new PerformanceCounter(categoryName, "Pending Async Calls", readOnly: false);
while (true)
{
perfPendingAsyncCalls.RawValue = _PendingAsyncCalls;
Thread.Sleep(500);
}
}
catch (ThreadAbortException)
{
}
catch (Exception ex)
{
ExceptionHandler.LogException(ex);
}
}
public IAsyncResult BeginProcessRequest(HttpContext context, AsyncCallback cb, object extraData)
{
if (SynchronousExecute(context))
{
return new SynchronousCompletionAsyncResult(cb, extraData);
}
Interlocked.Increment(ref _PendingAsyncCalls);
FastAsyncResult asyncResult = new FastAsyncResult(cb, extraData);
CcrService.Singleton.SpawnIterator(context, asyncResult, ExecuteAndComplete);
return asyncResult;
}
public void EndProcessRequest(IAsyncResult result)
{
if (result is FastAsyncResult fastResult)
{
Exception error = fastResult.Error;
fastResult.Dispose();
Interlocked.Decrement(ref _PendingAsyncCalls);
if (error != null)
{
throw new ApplicationException("Roblox.Ccr.HttpHandler Error", error);
}
}
}
public void ProcessRequest(HttpContext context)
{
throw new NotImplementedException();
}
}
@@ -0,0 +1,76 @@
using System;
using System.Collections.Generic;
using System.Web;
using Microsoft.Ccr.Core;
using Roblox.Common;
namespace Roblox.Ccr;
public abstract class HttpModule : IHttpModule
{
protected abstract IEnumerator<ITask> Execute(HttpApplication httpApplication);
private IAsyncResult BeginPreRequestHandlerExecute(object source, EventArgs e, AsyncCallback callback, object state)
{
HttpApplication t = (HttpApplication)source;
FastAsyncResult result = new FastAsyncResult(callback, state);
CcrService.Singleton.SpawnIterator(t, result, ExecuteAndComplete);
return result;
}
private void EndPreRequestHandlerExecute(IAsyncResult result)
{
if (result is FastAsyncResult fastResult)
{
Exception error = fastResult.Error;
fastResult.Dispose();
if (error != null)
{
throw new ApplicationException("Roblox.Ccr.HttpModule Error", error);
}
}
}
private IEnumerator<ITask> ExecuteAndComplete(HttpApplication httpApplication, FastAsyncResult result)
{
IEnumerator<ITask> en;
try
{
en = Execute(httpApplication);
}
catch (Exception completed2)
{
result.SetCompleted(completed2);
yield break;
}
using (en)
{
while (true)
{
try
{
if (!en.MoveNext())
{
result.SetCompleted();
break;
}
}
catch (Exception completed)
{
result.SetCompleted(completed);
break;
}
yield return en.Current;
}
}
}
public void Dispose()
{
}
public void Init(HttpApplication application)
{
application.AddOnPreRequestHandlerExecuteAsync(BeginPreRequestHandlerExecute, EndPreRequestHandlerExecute);
}
}