Dependencies
The bulk of work for this month has been getting rid of the Memory dependency from the Http library.
This is incredibly important, because allocation is only done by the Memory library!
This means that excluding Containers and the support ContainersReflection all libraries make No allocation whatsoever.
All of them can work inside fixed buffers or inside dynamically allocated memory allocated by the user that has knowledge of the entire application context.
Plus, it finally makes the internal dependencies graph between all libraries look super cool:
Http
Going back to the actual improvements to Http, I am not sure where to start. Multiple iterations of the API have been done and it's probably not over yet...
The API at the beginning of the month was something like this, with allocating Buffers usage and raw allocations everywhere (so ugly!):
The point has been making sure that a connection should be able to live within a block of memory that is fixed during its initialization.
This is because most of the time, reasonable defaults and safety limits must be set anyway to control how many Kb of headers can be accepted or how many Kb can be dedicated to streaming large files.
One example on initializing the web server with compile time fixed buffers is the following:
As AsyncStreams have been fully integrated, it's now possible to send even very large file using some tiny fixed buffers.
If they're too tiny, the transfer is going to become very slow, as a lot of syscall will be needed to fully transmit the file, so it's always as good idea sizing the buffers accordingly.
If one wants to decide the size of these buffers at runtime, the API will become slightly more verbose but I would say it's not so bad:
The example shows how to assign memory from Structure Of Arrays style arrangement for these buffers, all equally sized.
This is using an helper class called StableArray that uses VirtualMemory to reserve space for extremely large buffers and just commits to physical RAM the actually used quantity.
The WebServerExample in SCExample also shows how to resize such buffers at runtime, while the web-server is running!
It's also possible to just give each connection a buffers of different sizes, making the memory management slightly more complicated.
The library is still in 🟥 Draft state, and it will likely stay like that for the next months.
I can still see some random lost connections in browser tools from Safari / Firefox / Chrome when trying to visit some test website.
Also the performance / latency looks quite bad for now, but it doesn't make sense to start working on performance before becoming compliant with the spec to some acceptable level!
Detailed list of commits:
- Http: Add helper to configure connection buffers and queues at runtime
- Http: Allow declaring fixed size queues side by side with HttpConnection
- Http: Allow resizing server connections array
- Http: Allow user to setup header memory for each connection
- Http: Improve Documentation
- Http: Let caller supply the streams for async file server stream
- Http: Make buffers pool and pipeline per connection
- Http: Make HttpAsyncServer and HttpAsyncFileServer API more symmetric
- Http: Make ThreadPool mandatory to initialize HttpAsyncFileServer
- Http: Move pipeline from file stream to connection object
- Http: Move slice buffers helper to AsyncStreams
- Http: Parse response in HttpClient to read Content-Length
- Http: Remove dependency on Memory (drop HttpClient)
- Http: Rename HttpServer to HttpConnectionsPool
- Http: Rename HttpWebServer to HttpAsyncFileServer
- Http: Use AsyncStreams in HttpResponse
- Http: Use AsyncStreams in HttpWebServer
- Http: Write response headers to the headers buffer
- SCExample: Allow changing buffers sizes at runtime in WebServer Example
- SCExample: Use virtual memory for all web server buffers
AsyncStreams
A few changes in AsyncStreams have been made, as consequences of Http improvements.
Writable streams can now be destroyed and pipelines are automatically un-piped after all sinks have finished writing their data.
A few fixes also have been applied, like copying listeners during emit event (to allow changing listeners when inside an handler), emptying the read queue on destroy and resetting the request state on init.
Methods to set queues and buffers for the pool have been separated from initialization, so that they can be set during construction / setup and re-used across multiple successive initializations.
Detailed list of commits:
- AsyncStreams: Add destroy method for writable stream
- AsyncStreams: Add explicit method to set buffers in the pool
- AsyncStreams: Add method to explicitly set read and write queues
- AsyncStreams: Automatically un-pipe pipelines after all sinks have finished
- AsyncStreams: Copy listeners during Event emit
- AsyncStreams: Empty the readQueue when destroying
- AsyncStreams: Explicitly mark buffers to be re-used when their refCount goes to zero
- AsyncStreams: Reset requests state on stream initialization
Async
The extensive testing and improvements of Http library has exposed some issues in Async.
A quite nasty bug in the gather writes has been fixed in Posix, affecting partial writes, that has never been surfacing in the unit tests.
Also it made sense to ignore reporting errors when cancellations fail to submit on windows. This happens when trying to cancel an async operation that has already finished but for which the overlapped status has not been retrieved yet.
Detailed list of commits:
- Async: Add method to check if event loop backend needs thread-pool
- Async: Do not report errors when cancellations fail to submit
- Async: Fix incomplete gather writes on Posix
Others
The usual mix of fixes this month includes a debug visualizer for Span<T> and properly support exporting types across dynamic libraries (by adding SC_COMPILER_EXPORT in all relevant places).
During a bad search and replace done a few months ago, Result lost its [[nodiscard]] status, that has been fixed.
GrowableBuffer has now a Span<char> specialization, to allow using things like StringFormat / StringBuilder with raw char arrays.
VirtualMemory class has been cleaned up and improved, with its destructor properly releasing allocated memory.
A bug in Process was reading incomplete output from child processes terminating very quickly.
Detailed list of commits:
- DebugVisualizer: Add debug visualizers for Span
- Everywhere: Add SC_COMPILER_EXPORT clause to many types
- File: Add method to read into a fixed buffer until it's full or EOF happens
- Foundation: Add GrowableBuffer> specialization
- Foundation: Mark Result as a [[nodiscard]] type again
- Memory: Make VirtualMemory fields private and add destructor
- Process: Read entire process output until in user supplied writable span
See you next month!