mirror of
https://github.com/copyrighttxt/watrbx-game-engine.git
synced 2026-09-06 13:47:48 +00:00
GEEKING
This commit is contained in:
@@ -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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user