Description
Hi! I'm trying to make some unofficial Rust bindings for libunit.a
(see unit-rs), and I have some questions about the C API.
Right now my bindings are pretty bad. It seems there's several things I got wrong and I might have several misconceptions that are making my bindings a lot more restrictive than they should be. Still, I am really impressed with the technical aspects of Unit and how it works, and it's been really fun to work with it, and I'd like to rework my bindings to better match the API's capabilities.
If anyone answers any of these questions, I'd like to improve the descriptions in the nxt_unit.h
header. Would a PR for that be accepted?
The questions:
Multi-threading and thread local storage
Assuming that contexts and requests are only accessed with a locked mutex, can Unit's C API functions be called from a different thread than the thread which created the context/request? In other words, do the context/request objects rely on thread-specific things like variables in thread-local-storage?
More specifically...
nxt_unit_init()
returns a context object that must then be destroyed with nxt_unit_done()
. Can nxt_unit_done()
be called from a different thread?
nxt_unit_ctx_alloc()
creates a secondary context based on the main context. Can nxt_unit_done()
be called from a different thread than the one which created the context?
Can nxt_unit_run()
be called on a different thread than the one which created the context?
The request_handler()
callback will be called on the thread that runs nxt_unit_run()
, and it will be given a request object. Can methods that use this request object (like nxt_unit_response_send
, nxt_unit_response_buf_alloc
, etc) be called on a different thread than the one which received the request object?
If I get a request from nxt_unit_dequeue_request()
, can I send that request to a different thread and call API functions on it there?
Request body streaming
From my experiments, Unit supports a max of 8MB bodies, buffers the whole body, and then calls this data_handler()
callback at most once. Is that correct, or should I expect it to be called multiple times for slow-writing clients?
Also from my experiments, if data_handler()
is to be called, then before that, in request_handler()
, the nxt_unit_request_read()
API always returns 0 bytes. Is that always the case? Does nxt_unit_request_read()
always return all or nothing? Or can I expect partial results?
I don't see blocking/non-blocking variants for nxt_unit_request_read()
. Can I safely assume that nxt_unit_request_read()
is always non-blocking?
Is the NXT_UNIT_AGAIN
error code related in any way to the above?
Is the nxt_unit_app_test.c
example incorrect for requests with large request bodies?
Clean shutdown
Let's say a thread wants to quit (e.g. it experienced a fatal error). Is my only option to exit()
the process? Is there any way to trigger a graceful shutdown of this process, so that all other threads can finish whatever request they are handling, and then be given a QUIT
message?
Also, what happens if nxt_unit_done()
is called on the main context when there are still secondary contexts created from the main one? Will they cleanly shut down, or is this undefined behavior?
Does the main context have to live for at least as long as the contexts spawned from it, or can it be done
'd earlier?
Request response buffers
Can nxt_unit_response_buf_alloc()
be called multiple times before sending one of the buffers? In other words, can multiple buffers exist at the same time?
Can I send response buffers in reverse order?
What is nxt_unit_buf_next()
for? Does its result affect nxt_unit_buf_send()
in any way?
Is it safe to call nxt_unit_request_done()
on a request before sending or deallocating all of the buffers? If yes, will the buffers be automatically deallocated?
Since there is a non-blocking version of nxt_unit_response_write()
, then I assume nxt_unit_response_write()
is the blocking variant. When this blocks, the entire thread will be unavailable to process other requests. Is this vulnerable to clients with slow-reading, or will the Unit server accept and buffer the whole response even if the client doesn't read it?
Does nxt_unit_buf_send()
block? If yes, is it susceptible to slow-reading clients? Does it ever return NXT_UNIT_AGAIN
?
Misc questions
When is the close_handler()
callback ever called? Is that only for websockets?
How do nxt_unit_run()
, nxt_unit_run_ctx()
, and nxt_unit_run_shared()
differ?
If I call nxt_unit_malloc()
on one context, can I call nxt_unit_free()
on a different context?
What is NXT_UNIT_AGAIN
for, and what returns this? Can I return or send this myself from anywhere?