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.
To avoid closing the body fd prematurely, the fd value is moved from
the request struct to the app link. The body fd should not be closed
immediately after the request is sent to the application due to possible
request rescheduling.
Router built with debug may stop with assertion during stalled requests
re-schedule. This was caused by missing reference counting increment
before nxt_router_port_select() call.
Uninitialized ctx_impl field may cause crash in application process.
To reproduce the issue, need to trigger shared memory buffer send error on
application side. In our case, send error caused by router process crash.
This issue was introduced in 2c7f79bf0a1f.
Re-scheduled req_app_link structures should have use_count exactly equal
to the number of references from the application and port list. However,
there's one extra usage decrement that occurs after the req_app_link is
created because the use_count is initialised as 1.
This patch removes all excess instances of the usage decrement that caused
preliminary req_app_link release and router process crash.
To reproduce the issue need to cause request rescheduling between 2 app
processes.
This issue was introduced in 61e9f23a566d.
Each request processed in a separate goroutine. In case of OOSM state,
during response write, request goroutine blocks on channel which waits
event from main thread about SHM_ACK message from router.
ServerResponse.write() method tries to write data buffer using libunit
and stores buffers to write in a Server-wide output queue, which is
processed in response to SHM_ACK message from router.
As a side effect 'drain' event implemented and socket.writable flag
reflect current state.
- OOSM (out of shared memory). Sent by application process to router
when application reaches the limit of allocated shared memory and
needs more.
- SHM_ACK. Sent by router to application when the application's shared
memory is released and the OOSM flag is enabled for the segment.
This implements blocking mode (the library waits for SHM_ACK in case of
out of shared memory condition and retries allocating the required memory
amount) and non-blocking mode (the library notifies the application that
it's out of shared memory and returns control to the application module
that sets up the output queue and puts SHM_ACK in the main message loop).
Current shared memory buffer implementation uses fixed-size memory blocks,
allocating at least 16384 bytes. When application sends data in a large
number of small chunks, it makes sense to buffer them or use plain
memory buffers to improve performance and reduce memory footprint.
This patch introduces minimum size limit (1024 bytes) for shared
memory buffers.
To support Go modules, the unit-go module should be platform independent.
All platform-dependent logic is moved to libunit, so the unit-dev package
should be installed before building unit-go.
Temporary file name with configure process PID used to download
JAR from external repository. Then file renamed using command 'mv'.
The issue reproduced in clean environment when 2 or more concurrent builds
started.
The reason for the change is that the req_app_link reference count
was incorrect if the application crashed at start; in this case,
the nxt_request_app_link_update_peer() function was never called.
This closes#332 issue on GitHub.
There was a change (ruby/ruby@6c70fed) in Ruby 2.6 that moved
RUBY_DESCRIPTION global constant definition out of Init_version().
Unit initialized Ruby incorrectly, so the constant was not defined.
This closes#330 issue on GitHub.
Name and value in each header are 0-terminated, so additional 2 bytes
should be allocated for them. There were several attempts to add these
2 bytes to headers in language modules, but some modules weren't updated.
Also, adding these 2 bytes is specific to the implementation which may be
changed later, so extending this mechanics to modules may cause errors.
Python 3.8 has 'tp_print' field in PyTypeObject struct. This field is
attributed as deprecated. So, clang generates warning (which is turned to
error) as a result of initializing this field. From the other hand, it is
impossible to omit this field in positional initialization. The solution
is to use designated initializer.
Silencing usage message during configure python.
This is related to #331 issue on GitHub.
There was a typo: nxt_queue_head() used instead of nxt_queue_first() in
connection iteration loop. This prevents idle connection close on quit.
This closes#334 issue on GitHub.
Thanks to 洪志道 (Hong Zhi Dao).
Currently almost all Unit object files depends on generated nxt_version.h.
This patch adds missing dependence and fixes running make with multiple
jobs.
This closes#318 issue on GitHub.
Header names and values are stored 0-terminated for ease of use in different
languages, so magic number 2 should be added to each name-value pair size.
When Unit starts, the main process waits for module discovery message for a
while. If a QUIT signal arrives at this time, the router and controller
processes created by main and Unit stay running. Also, the main process
doesn't stop them after the second QUIT signal is received in this case.
Each request references the router process structure that owns all memory
maps. The process structure has a reference counter; each request increases
the counter to lock the structure in memory until request processing ends.
Incoming and outgoing buffers reference memory maps that the process owns,
so the process structure should be released only when all buffers are
released to avoid invalid memory access and a crash.
This describes the libunit library mechanism used for application processes.
The background of this issue is as follows:
The issue was found on buildbot when the router crashed during Java
websocket tests. The Java application receives a notification from the
master process; when the notification is processed, libunit deletes the
process structure from its process hash and decrements the use counter;
however, active websocket connections maintain their use counts on the
process structure. After that, when the master process is stopping the
application, libunit releases active websocket connections. At this point,
it's important to release the connections' memory buffers before the
corresponding process structure and all shared memory segments are released.
To pass Go object references to C and back we use hack with casting to
unsafe and then to uintptr. However, we should not store such references
because Go not guaratnee it will be available by the same address.
Introducing map with integer key helps to avoid dereference stored address.
This closes#253 and #309 issues on GitHub.
By design, Unit context is created for the thread which reads messages from
the router. However, Go request handlers are called in a separate goroutine
that may be executed in a different thread. To avoid a racing condition,
access to lists of free structures in the context should be serialized. This
patch should fix random crashes in Go applications under high load.
This is related to #253 and #309 issues on GitHub.
The "nxt_http_websocket" request state, defined in "nxt_http_websocket.c",
is used in "nxt_router.c" and must be linked with external symbol declared
in "nxt_router.c".
Due to the missing "extern" keyword, building Unit with some linkers
(notably gold and LLD) caused WebSocket connections to get stuck or even
crash the router process.
All allocated blocks for lvlhash required to be aligned because lower
address bits used for various extra information. Using unaligned blocks
may cause invalid memory aceess.
This was issue found on buildbot running large configuration tests.