diff --git a/Unhinged.Playground/Program.cs b/Unhinged.Playground/Program.cs index f979047..eff6d54 100644 --- a/Unhinged.Playground/Program.cs +++ b/Unhinged.Playground/Program.cs @@ -41,11 +41,11 @@ private static void RequestHandler(Connection connection) private static readonly JsonContext SerializerContext = JsonContext.Default; private static void CommitJsonResponse(Connection connection) { - connection.WriteBuffer.Write("HTTP/1.1 200 OK\r\n"u8 + - "Server: W\r\n"u8 + - "Content-Type: application/json; charset=UTF-8\r\n"u8 + - "Content-Length: 27\r\n"u8); - connection.WriteBuffer.Write(DateHelper.HeaderBytes); + connection.WriteBuffer.WriteUnmanaged("HTTP/1.1 200 OK\r\n"u8 + + "Server: W\r\n"u8 + + "Content-Type: application/json; charset=UTF-8\r\n"u8 + + "Content-Length: 27\r\n"u8); + connection.WriteBuffer.WriteUnmanaged(DateHelper.HeaderBytes); t_utf8JsonWriter ??= new Utf8JsonWriter(connection.WriteBuffer, new JsonWriterOptions { SkipValidation = true }); t_utf8JsonWriter.Reset(connection.WriteBuffer); @@ -58,13 +58,13 @@ private static void CommitJsonResponse(Connection connection) private static void CommitPlainTextResponse(Connection connection) { - connection.WriteBuffer.Write("HTTP/1.1 200 OK\r\n"u8 + - "Server: W\r\n"u8 + - "Content-Type: text/plain\r\n"u8 + - //"Content-Length: 13\r\n\r\nHello, World!"u8); - "Content-Length: 13\r\n"u8); + connection.WriteBuffer.WriteUnmanaged("HTTP/1.1 200 OK\r\n"u8 + + "Server: W\r\n"u8 + + "Content-Type: text/plain\r\n"u8 + + //"Content-Length: 13\r\n\r\nHello, World!"u8); + "Content-Length: 13\r\n"u8); connection.WriteBuffer.WriteUnmanaged(DateHelper.HeaderBytes); - connection.WriteBuffer.Write("Hello, World!"u8); + connection.WriteBuffer.WriteUnmanaged("Hello, World!"u8); } } diff --git a/Unhinged/ABI/ProcessorArchDependant.cs b/Unhinged/ABI/ProcessorArchDependant.cs index 7da223a..4873e80 100644 --- a/Unhinged/ABI/ProcessorArchDependant.cs +++ b/Unhinged/ABI/ProcessorArchDependant.cs @@ -107,6 +107,34 @@ internal static void ReadEpollEvent(void* src, out uint events, out int fd) fd = (int)*(uint*)((byte*)src + 8); } } + + // Variations, TODO: Test performance required + + [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] + internal static void WriteEpollEvent2(void* dest, uint events, int fd) + { + // Write events (always aligned 4B store) + *(uint*)dest = events; + + // Compute data offset (packed: +4, natural: +8) + var data = (byte*)dest + (Packed ? 4 : 8); + + // Store only low 32 bits of fd and zero the high 32 bits. + // Using two 4B stores avoids an unaligned 8B write in the packed layout. + *(uint*)data = (uint)fd; // low 32 + *(uint*)(data + 4) = 0; // high 32 + } + + [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] + internal static void ReadEpollEvent2(void* src, out uint events, out int fd) + { + events = *(uint*)src; + + var data = (byte*)src + (Packed ? 4 : 8); + + // We only ever wrote the low 32 bits; read exactly those. + fd = (int)*(uint*)data; + } // ============================================================================================= // Networking helpers diff --git a/Unhinged/Engine/UnhingedEngine.Acceptor.cs b/Unhinged/Engine/UnhingedEngine.Acceptor.cs index 5047ac8..93213a0 100644 --- a/Unhinged/Engine/UnhingedEngine.Acceptor.cs +++ b/Unhinged/Engine/UnhingedEngine.Acceptor.cs @@ -88,7 +88,7 @@ private static void AcceptorLoop(int listenFd, Worker[] workers) workers[w].Inbox.Enqueue(cfd); // hand off fd to worker queue Interlocked.Increment(ref workers[w].Current); // bump worker load metric - //Console.WriteLine($"Incremented {workers[w].Ep} with cfg {cfd} to {workers[w].Current}"); + Console.WriteLine($"Incremented {workers[w].Ep} with cfg {cfd} to {workers[w].Current}"); // Wake the worker via eventfd. We write 8 bytes (uint64). ulong inc = 1; diff --git a/Unhinged/Engine/UnhingedEngine.Worker.cs b/Unhinged/Engine/UnhingedEngine.Worker.cs index 2244f89..0ac5ffa 100644 --- a/Unhinged/Engine/UnhingedEngine.Worker.cs +++ b/Unhinged/Engine/UnhingedEngine.Worker.cs @@ -4,6 +4,8 @@ // ReSharper disable always StackAllocInsideLoop // ReSharper disable always ClassCannotBeInstantiated +using System.Text; + #pragma warning disable CA2014 namespace Unhinged; @@ -129,13 +131,12 @@ private static void WorkerLoop(Worker W) long got; //fixed (byte* p = &c.Buf[c.Tail]) // got = recv(fd, (IntPtr)p, (ulong)avail, 0); - got = recv(fd, c.ReceiveBuffer, (ulong)avail, 0); + got = recv(fd, c.ReceiveBuffer + c.Tail, (ulong)avail, 0); if (got > 0) { c.Tail += (int)got; continue; - } if (got == 0) { CloseConn(fd, connections, W); break; } // peer closed @@ -226,6 +227,7 @@ private static bool TryParseRequests(Connection connection) //int idx = FindCrlfCrlf(connection.Buf, connection.Head, connection.Tail); //int idx = FindCrlfCrlf(connection.ReceiveBuffer, connection.Head, connection.Tail); var headerSpan = FindCrlfCrlf(connection.ReceiveBuffer, connection.Head, connection.Tail, out int idx); + if (idx < 0) break; @@ -240,12 +242,16 @@ private static bool TryParseRequests(Connection connection) // Mark that there is data to flush (a request was fully processed) hasDataToFlush = true; + + if (connection.Head == connection.Tail) + break; } // If there is unprocessed data in the receiving buffer (incomplete request) which is not at buffer start // Move the incomplete request to the buffer start and reset head and tail to 0 if (connection.Head > 0 && connection.Head < connection.Tail) { + Console.WriteLine("= Unprocessed data ="); Buffer.MemoryCopy( connection.ReceiveBuffer + connection.Head, connection.ReceiveBuffer, @@ -347,13 +353,20 @@ private static void CloseConn(int fd, Dictionary map, Worker W) { // Remove from map, close fd, and decrement the worker's load counter. // TODO: Defensive check if entry exists in the map before trying to remove it? - ConnectionPool.Return(map[fd]); - map.Remove(fd); + try + { + ConnectionPool.Return(map[fd]); + map.Remove(fd); - //Console.WriteLine($"Closing {fd}"); - - CloseQuiet(fd); - Interlocked.Decrement(ref W.Current); + //Console.WriteLine($"Closing {fd}"); + + CloseQuiet(fd); + Interlocked.Decrement(ref W.Current); + } + catch + { + // TODO: Complete this. + } } /// diff --git a/Unhinged/HttpProtocol/HeaderParsing.cs b/Unhinged/HttpProtocol/HeaderParsing.cs index 30c914f..cc193e2 100644 --- a/Unhinged/HttpProtocol/HeaderParsing.cs +++ b/Unhinged/HttpProtocol/HeaderParsing.cs @@ -3,6 +3,9 @@ // (var is avoided intentionally in this project so that concrete types are visible at call sites.) // ReSharper disable always StackAllocInsideLoop // ReSharper disable always ClassCannotBeInstantiated + +using System.Text; + #pragma warning disable CA2014 namespace Unhinged; @@ -61,6 +64,9 @@ internal static ReadOnlySpan FindCrlfCrlf(byte* buf, int head, int tail, o // The caller must guarantee that (tail - head) bytes are valid and readable. var span = new ReadOnlySpan(buf + head, tail - head); + //Console.WriteLine($"Spitting buffer: {head} {tail} {tail-head}"); + //Console.WriteLine(Encoding.UTF8.GetString(span)); + idx = span.IndexOf(CrlfCrlf); if (idx >= 0) idx += head; diff --git a/Unhinged/Unhinged.csproj b/Unhinged/Unhinged.csproj index f881e33..226a219 100644 --- a/Unhinged/Unhinged.csproj +++ b/Unhinged/Unhinged.csproj @@ -10,9 +10,9 @@ Diogo Martins https://github.com/MDA2AV/Unhinged git - 9.0.1 - 9.0.1 - 9.0.1 + 9.0.2 + 9.0.2 + 9.0.2 README.md Unhinged LICENSE