diff --git a/tests/context.cpp b/tests/context.cpp index c8e8267..2227635 100644 --- a/tests/context.cpp +++ b/tests/context.cpp @@ -18,6 +18,25 @@ TEST_CASE("context create, close and destroy", "[context]") CHECK(NULL == (void *) context); } +TEST_CASE("context shutdown", "[context]") +{ + zmq::context_t context; + context.shutdown(); + CHECK(NULL != (void *) context); + context.close(); + CHECK(NULL == (void *) context); +} + +TEST_CASE("context shutdown again", "[context]") +{ + zmq::context_t context; + context.shutdown(); + context.shutdown(); + CHECK(NULL != (void *) context); + context.close(); + CHECK(NULL == (void *) context); +} + #ifdef ZMQ_CPP11 TEST_CASE("context swap", "[context]") { @@ -26,4 +45,22 @@ TEST_CASE("context swap", "[context]") using std::swap; swap(context1, context2); } + +TEST_CASE("context - use socket after shutdown", "[context]") +{ + zmq::context_t context; + zmq::socket_t sock(context, zmq::socket_type::rep); + context.shutdown(); + try + { + sock.connect("inproc://test"); + zmq::message_t msg; + sock.recv(msg, zmq::recv_flags::dontwait); + REQUIRE(false); + } + catch (const zmq::error_t& e) + { + REQUIRE(e.num() == ETERM); + } +} #endif diff --git a/zmq.hpp b/zmq.hpp index 87d7b31..0aafe8b 100644 --- a/zmq.hpp +++ b/zmq.hpp @@ -677,6 +677,7 @@ class context_t ~context_t() ZMQ_NOTHROW { close(); } + // Terminates context (see also shutdown()). void close() ZMQ_NOTHROW { if (ptr == ZMQ_NULLPTR) @@ -691,6 +692,17 @@ class context_t ptr = ZMQ_NULLPTR; } + // Shutdown context in preparation for termination (close()). + // Causes all blocking socket operations and any further + // socket operations to return with ETERM. + void shutdown() ZMQ_NOTHROW + { + if (ptr == ZMQ_NULLPTR) + return; + int rc = zmq_ctx_shutdown(ptr); + ZMQ_ASSERT(rc == 0); + } + // Be careful with this, it's probably only useful for // using the C api together with an existing C++ api. // Normally you should never need to use this.