0
0
mirror of https://github.com/zeromq/libzmq.git synced 2024-12-26 23:01:04 +08:00

Problem: sockets can be created after calling zmq_ctx_shutdown

Solution: fix handling of _starting and _terminate flags

Add tests for this situation.

Clarify documentation of zmq_ctx_shutdown and zmq_socket.

Fixes #3792
This commit is contained in:
Simon Giesecke 2020-01-26 18:04:06 +01:00
parent 349e3e21db
commit 36a8df2f8c
4 changed files with 63 additions and 17 deletions

View File

@ -19,7 +19,9 @@ The _zmq_ctx_shutdown()_ function shall shutdown the 0MQ context 'context'.
Context shutdown will cause any blocking operations currently in progress on
sockets open within 'context' to return immediately with an error code of ETERM.
With the exception of _zmq_close()_, any further operations on sockets open within
'context' shall fail with an error code of ETERM.
'context' shall fail with an error code of ETERM. No further sockets can be created
using _zmq_socket()_ on a context for which _zmq_ctx_shutdown()_ has been called,
it will return and set errno to ETERM.
This function is optional, client code is still required to call the linkzmq:zmq_ctx_term[3]
function to free all resources allocated by zeromq.

View File

@ -556,7 +556,7 @@ The provided 'context' is invalid.
*EMFILE*::
The limit on the total number of open 0MQ sockets has been reached.
*ETERM*::
The context specified was terminated.
The context specified was shutdown or terminated.
EXAMPLE
-------

View File

@ -232,16 +232,18 @@ int zmq::ctx_t::shutdown ()
{
scoped_lock_t locker (_slot_sync);
if (!_starting && !_terminating) {
if (!_terminating) {
_terminating = true;
// Send stop command to sockets so that any blocking calls
// can be interrupted. If there are no sockets we can ask reaper
// thread to stop.
for (sockets_t::size_type i = 0; i != _sockets.size (); i++)
_sockets[i]->stop ();
if (_sockets.empty ())
_reaper->stop ();
if (!_starting) {
// Send stop command to sockets so that any blocking calls
// can be interrupted. If there are no sockets we can ask reaper
// thread to stop.
for (sockets_t::size_type i = 0; i != _sockets.size (); i++)
_sockets[i]->stop ();
if (_sockets.empty ())
_reaper->stop ();
}
}
return 0;
@ -471,17 +473,18 @@ zmq::socket_base_t *zmq::ctx_t::create_socket (int type_)
{
scoped_lock_t locker (_slot_sync);
if (unlikely (_starting)) {
if (!start ())
return NULL;
}
// Once zmq_ctx_term() was called, we can't create new sockets.
// Once zmq_ctx_term() or zmq_ctx_shutdown() was called, we can't create
// new sockets.
if (_terminating) {
errno = ETERM;
return NULL;
}
if (unlikely (_starting)) {
if (!start ())
return NULL;
}
// If max_sockets limit was reached, return error.
if (_empty_slots.empty ()) {
errno = EMFILE;

View File

@ -88,7 +88,46 @@ void test_ctx_shutdown ()
// Close the socket.
TEST_ASSERT_SUCCESS_ERRNO (zmq_close (socket));
// Destory the context, will now not hang as we have closed the socket.
// Destroy the context, will now not hang as we have closed the socket.
TEST_ASSERT_SUCCESS_ERRNO (zmq_ctx_destroy (ctx));
}
void test_ctx_shutdown_socket_opened_after ()
{
// Set up our context.
void *ctx = zmq_ctx_new ();
TEST_ASSERT_NOT_NULL (ctx);
// Open a socket to start context, and close it immediately again.
void *socket = zmq_socket (ctx, ZMQ_PULL);
TEST_ASSERT_NOT_NULL (socket);
TEST_ASSERT_SUCCESS_ERRNO (zmq_close (socket));
// Shutdown context.
TEST_ASSERT_SUCCESS_ERRNO (zmq_ctx_shutdown (ctx));
// Opening socket should now fail.
TEST_ASSERT_NULL (zmq_socket (ctx, ZMQ_PULL));
TEST_ASSERT_FAILURE_ERRNO (ETERM, -1);
// Destroy the context.
TEST_ASSERT_SUCCESS_ERRNO (zmq_ctx_destroy (ctx));
}
void test_ctx_shutdown_only_socket_opened_after ()
{
// Set up our context.
void *ctx = zmq_ctx_new ();
TEST_ASSERT_NOT_NULL (ctx);
// Shutdown context.
TEST_ASSERT_SUCCESS_ERRNO (zmq_ctx_shutdown (ctx));
// Opening socket should now fail.
TEST_ASSERT_NULL (zmq_socket (ctx, ZMQ_PULL));
TEST_ASSERT_FAILURE_ERRNO (ETERM, -1);
// Destroy the context.
TEST_ASSERT_SUCCESS_ERRNO (zmq_ctx_destroy (ctx));
}
@ -201,6 +240,8 @@ int main (void)
UNITY_BEGIN ();
RUN_TEST (test_ctx_destroy);
RUN_TEST (test_ctx_shutdown);
RUN_TEST (test_ctx_shutdown_socket_opened_after);
RUN_TEST (test_ctx_shutdown_only_socket_opened_after);
RUN_TEST (test_zmq_ctx_term_null_fails);
RUN_TEST (test_zmq_term_null_fails);
RUN_TEST (test_zmq_ctx_shutdown_null_fails);