The PORT_ACK message is the router's response to the application's NEW_PORT
message. After receiving PORT_ACK, the application is safe to process requests
using this port.
This message avoids a racing condition when the application starts processing a
request from the shared queue and sends REQ_HEADERS_ACK. The REQ_HEADERS_ACK
message contains the application port ID as reply_port, which the router uses
to send request data. When the application creates a new port, it
immediately sends it to the main router thread. Because the request is
processed outside the main thread, a racing condition can occur between the
receipt of the new port in the main thread and the receipt of REQ_HEADERS_ACK
in the worker router thread where the same port is specified as reply_port.
This fixes the router's crash on buildbot; the reason was an unexpected 'last'
response from the application to the router arriving before the response
headers. The last buffer is not a memory buffer, so the result of accessing
memory fields is unpredictable.
The unexpected 'last' message was caused by an error in libunit; fixed in
fee8fd855a00.
Data in the queue and the socket are transmitted independently; special
READ_QUEUE and READ_SOCKET message types are used for synchronization.
The warning was accidentally committed with changeset 1d84b9e4b459.
The connection's local socket address is allocated from the connection
pool before the request is passed to the application; however, with keep-alive
connections, this field was unconditionally reset by a socket configuration
value that could be NULL. For the next request, the address was allocated
again from the same connection pool. Nonetheless, all leaked addresses
were released when the connection was closed.
The issue introduced in changeset 5c7dd85fabd5.
Large-bodied requests are added to the request hash to be found when the body
arrives. However, changeset 1d84b9e4b459 introduced a bug: the 'in_hash' flag,
used to remove the request from the hash at request release, was cleared after
the first successful request lookup. As a result, the entry was never removed.
This change aids heap usage analysis in applications.
The alloc and free functions are also required for lvlhash due to the upcoming
threading support, because using main nxt_memalign() and nxt_free() isn't safe
in a multithreaded app environment. The reason is that these functions may use
thread-local structures which aren't initialized properly in applications.
The issue was introduced in changeset 1d84b9e4b459. The request buffer was
transferred via the shared application queue, but the buffer position and the
'sent' flag were not updated after the buffer had been sent.
The Apache HTTP server benchmarking tool, ab, issues HTTP/1.0 requests with
the 'Connection: Keep-Alive' header and expects a 'Connection: Keep-Alive'
header in the response.
This is required for more flexible Python version check since ASGI works
for Python 3.5+. Version check via 'startswith()' function removed as
not consistent.
Now it is possible to specify the name of the application callable using
optional parameter 'callable'. Default value is 'application'.
This closes#290 issue on GitHub.
Buffer for application prefork request allocated from temp conf mem_pool.
If error response from main process received before buffer completion handler,
temp conf mem_pool destroyed and router may crash in completion handler.
Assertion "src/nxt_buf.c:208 assertion failed: data == b->parent" triggered
when NXT_DEBUG_ALLOC enabled in configure.
This patch disables completion handler and memory allocated for buffer
released with memory pool.
The default libunit behavior relies on blocking the recv() call for port file
descriptors, which an application may override if needed. For external
applications, port file descriptors were toggled to blocking mode before the
exec() call. If the exec() call failed, descriptor remained blocked, so the
process hanged while trying to read from it.
This patch moves file descriptor mode switch inside libunit.
This is a temporary solution after the 'shared port' patch. The application
process becomes idle immediately after creation. Even if it starts processing
a request (without acknowledging it yet), it is stopped by the router because
an 'out-of-idle-time' event occurs.
Generic process-to-process shared memory exchange is no more required. Here,
it is transformed into a router-to-application pattern. The outgoing shared
memory segments collection is now the property of the application structure.
The applications connect to the router only, and the process only needs to group
the ports.
This is the port shared between all application processes which use it to pass
requests for processing. Using it significantly simplifies the request
processing code in the router. The drawback is 2 more file descriptors per each
configured application and more complex libunit message wait/read code.
The application process needs to request the shared memory segment from the
router instead of the latter pushing the segment before sending a request to
the application. This is required to simplify the communication between the
router and the application and to prepare the router for using the application
shared port and then the queue.
The application process needs to request the port from the router instead of the
latter pushing the port before sending a request to the application. This is
required to simplify the communication between the router and the application
and to prepare the router to use the application shared port and then the queue.
The goal is to minimize the number of (pid, id) to port hash lookups which
require a library mutex lock. The response port is found once per request,
while the read port is initialized at startup.
- Changed the port management callbacks to notifications, which e. g. avoids
the need to call the libunit function
- Added context and library instance reference counts for a safer resource
release
- Added the router main port initialization
If there are no listen sockets, the router configuration usage counter
remains 0 and never decreases. The only moment to release a configuration is
right after a configuration update.
According to libuv documentation, uv_poll_t memory should be released
in a callback function passed to uv_close(). Otherwise, the Node.js application
process may crash at exit.
A connection object is allocated in advance for each listen event object to be
used for the established connection. This connection needs to be freed when the
listen event is destroyed.
There is no restrictions on configration size and using segmented shared memory
only doubles memory usage because to parse configration on router side,
it needs to be 'plain' e. g. located in single continous memory buffer.
Currently, the router exits without waiting for the worker threads to stop.
There is a short gap between the runtime memory pool's free and the exit, during
which a worker thread may try to access a runtime structure. In turn, this may
cause a crash. For now, it is better to keep this memory allocated.
Correct value for non-initialized file descriptor is -1, because most of the
checks in libunit compares file descriptor with -1 before performing an
action. Using 0 as default value, may cause to close file descriptor #0, this
may affect application logic.
It is not required to list this patch in changelog because impact is not seen
by end users.
The nxt_assert macro uses nxt_thread_context, which caused the following linker
error when using it in the library:
ld: illegal thread local variable reference to regular symbol
_nxt_thread_context for architecture x86_64
After a process exits, all ports linked to it from other processes
should be closed. All unsent file descriptors in port queue, marked as
"close after send", should be closed to avoid resource leakage.
This is required due to lack of a graceful shutdown: there is a small gap
between the runtime's memory pool release and router process's exit. Thus, a
worker thread may start processing a request between these two operations,
which may result in an http fields hash access and subsequent crash.
To simplify issue reproduction, it makes sense to add a 2 sec sleep before
exit() in nxt_runtime_exit().
After 41331471eee7 completion handlers should complete next buffer in chain.
Otherwise buffer memory may leak.
Thanks to Peter Tkatchenko for reporing the issue and testing fixes.
An earlier attempt (ad6265786871) to resolve this condition on the
router's side added a new issue: the app could get a request before
acquiring a port.