Skip to content

Commit 64f54f4

Browse files
authored
Merge pull request #5 from MDA2AV/9.0.3
9.0.3
2 parents 0bd6f9f + ab66982 commit 64f54f4

16 files changed

Lines changed: 974 additions & 32 deletions

Unhinged.Playground/Program.cs

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,16 @@
88

99
#pragma warning disable CA2014
1010

11+
// dotnet publish -c Release /p:PublishAot=true /p:OptimizationPreference=Speed
12+
1113
[SkipLocalsInit]
1214
internal static class Program
1315
{
1416
public static void Main(string[] args)
1517
{
1618
var builder = UnhingedEngine
1719
.CreateBuilder()
18-
.SetNWorkersSolver(() => Environment.ProcessorCount / 2)
20+
.SetNWorkersSolver(() => (Environment.ProcessorCount / 2) - 2)
1921
.SetBacklog(16384)
2022
.SetMaxEventsPerWake(512)
2123
.SetMaxNumberConnectionsPerWorker(512)
@@ -28,13 +30,25 @@ public static void Main(string[] args)
2830
engine.Run();
2931
}
3032

31-
private static void RequestHandler(Connection connection)
33+
private static ValueTask RequestHandler(Connection connection)
3234
{
33-
if(connection.HashedRoute == 291830056) // /json
35+
/*if(connection.HashedRoute == 291830056) // /json
3436
CommitJsonResponse(connection);
3537
3638
else if (connection.HashedRoute == 3454831873) // /plaintext
39+
CommitPlainTextResponse(connection);*/
40+
41+
if (connection.H1HeaderData.Route.Equals("/json"))
42+
{
43+
CommitJsonResponse(connection);
44+
}
45+
46+
else if (connection.H1HeaderData.Route.Equals("/plaintext"))
47+
{
3748
CommitPlainTextResponse(connection);
49+
}
50+
51+
return ValueTask.CompletedTask;
3852
}
3953

4054
[ThreadStatic] private static Utf8JsonWriter? _tUtf8JsonWriter;

Unhinged/ABI/Native.cs

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -144,9 +144,12 @@ internal static unsafe class Native
144144
/// </summary>
145145
[DllImport("libc", SetLastError = true)] internal static extern int eventfd(uint initval, int flags);
146146

147-
[DllImport("libc", SetLastError = true)] internal static extern int sched_setaffinity(int pid, nuint cpusetsize, ref ulong mask);
148-
149-
147+
[DllImport("libc", SetLastError = true)] internal static extern int sched_setaffinity(int pid, IntPtr cpusetsize, ref ulong mask);
148+
149+
[DllImport("libc", SetLastError = true)] internal static extern int sched_setaffinity(int pid, IntPtr cpusetsize, ref cpu_set_t mask);
150+
151+
[DllImport("libc")] internal static extern int gettid(); // Linux thread id
152+
150153
// =========================
151154
// Struct definitions
152155
// =========================
@@ -264,4 +267,28 @@ internal struct Linger
264267
internal const int EPIPE = 32;
265268
internal const int ECONNABORTED = 103;
266269
internal const int ECONNRESET = 104;
270+
271+
public static void PinCurrentThreadToCpu(int cpuIndex)
272+
{
273+
if (cpuIndex < 0 || cpuIndex >= Environment.ProcessorCount)
274+
throw new ArgumentOutOfRangeException(nameof(cpuIndex));
275+
276+
unsafe
277+
{
278+
var set = new cpu_set_t();
279+
int word = cpuIndex / 64;
280+
int bit = cpuIndex % 64;
281+
set.Bits[word] = 1UL << bit;
282+
283+
int tid = gettid();
284+
int ret = sched_setaffinity(tid, (IntPtr)sizeof(cpu_set_t), ref set);
285+
if (ret != 0)
286+
throw new InvalidOperationException($"sched_setaffinity failed with errno {Marshal.GetLastPInvokeError()}");
287+
}
288+
}
267289
}
290+
291+
internal unsafe struct cpu_set_t
292+
{
293+
public fixed ulong Bits[16]; // 1024 bits (enough for up to 1024 CPUs)
294+
}

Unhinged/Engine/Connection.cs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,11 @@ public unsafe class Connection : IDisposable
2424
/// <summary>Writer over the send slab.</summary>
2525
public readonly FixedBufferWriter WriteBuffer;
2626

27-
// <summary>Fnv1a32 hashed route</summary>
28-
public uint HashedRoute { get; set; }
27+
/// <summary>
28+
/// Header data, no allocations
29+
/// </summary>
30+
public BinaryH1HeaderData BinaryH1HeaderData { get; set; }
31+
public H1HeaderData H1HeaderData { get; set; }
2932

3033
/// <param name="maxConnections">Used to size the slabs (typically per-worker slab size).</param>
3134
/// <param name="inSlabSize">Bytes per connection for receive.</param>
@@ -40,6 +43,11 @@ public Connection(int maxConnections, int inSlabSize, int outSlabSize)
4043
outSlabSize);
4144
}
4245

46+
public void Clear()
47+
{
48+
H1HeaderData?.Clear();
49+
}
50+
4351
/// <summary>
4452
/// Frees the unmanaged slabs. Call exactly once when the connection is permanently done.
4553
/// </summary>

Unhinged/Engine/UnhingedEngine.Builder.cs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,15 +42,17 @@ public sealed partial class UnhingedEngine
4242
private static Func<int>? _calculateNumberWorkers;
4343

4444
// Default request handler (overridden via builder). Writes a minimal plaintext response.
45-
private static Action<Connection> _sRequestHandler = DefaultRequestHandler;
45+
private static Func<Connection, ValueTask> _sRequestHandler = DefaultRequestHandler;
4646

47-
private static void DefaultRequestHandler(Connection connection)
47+
private static ValueTask DefaultRequestHandler(Connection connection)
4848
{
4949
connection.WriteBuffer.WriteUnmanaged("HTTP/1.1 200 OK\r\n"u8 +
5050
"Server: W\r\n"u8 +
5151
"Content-Type: text/plain\r\n"u8 +
5252
"Content-Length: 28\r\n\r\n"u8 +
5353
"Request handler was not set!"u8 );
54+
55+
return ValueTask.CompletedTask;
5456
}
5557

5658
private UnhingedEngine() { }
@@ -134,7 +136,7 @@ public UnhingedBuilder SetNWorkersSolver(Func<int>? solver)
134136
/// <summary>
135137
/// Inject the request handler used by workers to serve requests.
136138
/// </summary>
137-
public UnhingedBuilder InjectRequestHandler(Action<Connection> requestHandler)
139+
public UnhingedBuilder InjectRequestHandler(Func<Connection, ValueTask> requestHandler)
138140
{
139141
_sRequestHandler = requestHandler;
140142
return this;

Unhinged/Engine/UnhingedEngine.Runner.cs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,14 @@ public void Run()
4141
// - IsBackground=true so the process can exit if only workers remain.
4242
// - Name aids debugging and logs.
4343
// - Stack size is configurable to accommodate stackalloc-heavy hot paths.
44-
var t = new Thread(() => WorkerLoop(W[iCap]), _maxStackSizePerThread) // 1MB
44+
var t = new Thread(() =>
45+
{
46+
// Performance seems to be lower when pinning threads to cpu core
47+
//PinCurrentThreadToCpu(iCap);
48+
//Console.WriteLine($"Thread {iCap} pinned to CPU {iCap}");
49+
50+
WorkerLoop(W[iCap]);
51+
}, _maxStackSizePerThread) // 1MB
4552
{
4653
IsBackground = true,
4754
Name = $"worker-{iCap}"

0 commit comments

Comments
 (0)