Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions Libraries/SPTarkov.Server.Assets/SPT_Data/configs/core.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@
"profileSaveIntervalSeconds": 15,
"sptFriendNickname": "SPT",
"allowProfileWipe": true,
"enableNoGCRegions": true,
"noGCRegionMaxMemoryGB": 4,
"noGCRegionMaxLOHMemoryGB": 3,
"bsgLogging": {
"verbosity": 6,
"sendToServer": false
Expand Down
44 changes: 44 additions & 0 deletions Libraries/SPTarkov.Server.Core/Models/Spt/Config/CoreConfig.cs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,50 @@ public record CoreConfig : BaseConfig
[JsonPropertyName("features")]
public required ServerFeatures Features { get; set; }

[JsonPropertyName("enableNoGCRegions")]
// ReSharper disable once InconsistentNaming
public required bool EnableNoGCRegions { get; set; }

// ReSharper disable once InconsistentNaming
private int _noGCRegionMaxMemoryGB = 4;

[JsonPropertyName("noGCRegionMaxMemoryGB")]
// ReSharper disable once InconsistentNaming
public required int NoGCRegionMaxMemoryGB
{
get => _noGCRegionMaxMemoryGB;
set
{
if (value <= 0)
{
throw new ArgumentOutOfRangeException(
$"Invalid value: {nameof(NoGCRegionMaxMemoryGB)}: {value}. Must be greater than zero."
);
}
_noGCRegionMaxMemoryGB = value;
}
}

// ReSharper disable once InconsistentNaming
private int _noGCRegionMaxLOHMemoryGB = 3;

[JsonPropertyName("noGCRegionMaxLOHMemoryGB")]
// ReSharper disable once InconsistentNaming
public required int NoGCRegionMaxLOHMemoryGB
{
get => _noGCRegionMaxLOHMemoryGB;
set
{
if (value <= 0)
{
throw new ArgumentOutOfRangeException(
$"Invalid value {nameof(NoGCRegionMaxLOHMemoryGB)}: {value}. Must be greater than zero."
);
}
_noGCRegionMaxLOHMemoryGB = value;
}
}

/// <summary>
/// Commit hash build server was created from
/// </summary>
Expand Down
10 changes: 4 additions & 6 deletions SPTarkov.Server/Program.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System.Net;
using System.Net.Sockets;
using System.Runtime;
using System.Runtime.InteropServices;
using System.Security.Authentication;
using System.Text;
Expand Down Expand Up @@ -147,12 +148,9 @@ private static void ConfigureWebApp(WebApplication app)

app.UseMiddleware<SptLoggerMiddleware>();

app.Use(
async (HttpContext context, RequestDelegate next) =>
{
await context.RequestServices.GetRequiredService<HttpServer>().HandleRequest(context, next);
}
);
app.UseNoGCRegions();

app.Use(async (context, next) => await context.RequestServices.GetRequiredService<HttpServer>().HandleRequest(context, next));

app.UseSptBlazor();
}
Expand Down
80 changes: 80 additions & 0 deletions SPTarkov.Server/Services/NoGCRegionMiddleware.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
using System.Runtime;
using SPTarkov.Server.Core.Models.Spt.Config;
using SPTarkov.Server.Core.Servers;

namespace SPTarkov.Server.Services;

// ReSharper disable once InconsistentNaming
public class NoGCRegionMiddleware(RequestDelegate next)
{
private static long _activeRequests;

private static bool OtherRequestsActive
{
get
{
return Interlocked.Read(ref _activeRequests) > 1;
}
}

public async Task InvokeAsync(HttpContext context)
{
Interlocked.Increment(ref _activeRequests);

var config = context.RequestServices.GetRequiredService<ConfigServer>().GetConfig<CoreConfig>();

// if no other requests are running, start the no GC region, otherwise dont start it
if (!OtherRequestsActive)
{
if (config.EnableNoGCRegions && GCSettings.LatencyMode != GCLatencyMode.NoGCRegion)
{
try
{
GC.TryStartNoGCRegion(
1024L * 1024L * 1024L * config.NoGCRegionMaxMemoryGB,
1024L * 1024L * 1024L * config.NoGCRegionMaxLOHMemoryGB,
true
);
}
catch (Exception)
{
// ignored, we keep going
}
}
}
try
{
await next(context);
}
finally
{
Interlocked.Decrement(ref _activeRequests);
}

// if no other requests are running, end the no GC region, otherwise dont stop it as other requests need it still
if (!OtherRequestsActive)
{
if (config.EnableNoGCRegions && GCSettings.LatencyMode == GCLatencyMode.NoGCRegion)
{
try
{
GC.EndNoGCRegion();
}
catch (Exception)
{
// ignored, we dont care about handling this
}
}
}
}
}

// ReSharper disable once InconsistentNaming
public static class NoGCRegionMiddlewareExtensions
{
// ReSharper disable once InconsistentNaming
public static IApplicationBuilder UseNoGCRegions(this IApplicationBuilder builder)
{
return builder.UseMiddleware<NoGCRegionMiddleware>();
}
}
Loading