Merge pull request #3794 from sigiesec/fix-ctx-shutdown

Problem: sockets can be created after calling zmq_ctx_shutdown
This commit is contained in:
Luca Boccassi 2020-01-26 18:09:47 +00:00 committed by GitHub
commit 6d69898240
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
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);