[zeromq-dev] libzmq - allocations, exceptions, alloc_assert and such

Thomas Rodgers rodgert at twrodgers.com
Tue Jan 27 20:25:15 CET 2015

I know sustrik has previously railed <http://250bpm.com/blog:4> on this
topic, but at the risk of rehashing some of that ground...

My reading of libzmq's source suggests that it goes to some lengths to
prevent raising C++ exceptions, which, given that the C API functions do
not attempt to guard against propagating exceptions to the caller, is a
Good Thing. The code in some places uses malloc/free, in others either
placement new into pre-allocated memory or new (std::nothrow) to prevent
allocations from throwing. This is all well and good as far as it goes, but
it doesn't go that far -

stream_engine_t, mechanism_t, metadata_t, ctx_t (for example) all use
node-based STL containers (std::map, std::mutlimap) which perform
allocations on insertion/copy.

mechanism_t's ctor default instantiates two std::map<std::string,
std::string>'s. Properties are then added to these dictionaries as they are
consumed from the wire. Each property is stored as a key/value pair of
std::string, which may throw on construction if it cannot allocate the
requested space, and the insert may throw if it cannot allocate space for
the map node.

In stream_engine_t::mechanism_read() -

It creates a local std::map<std::string, std::string> which it then merges
properties exchanged from the transport mechanism (zap and zmtp) then
constructs a metadata_t with a copy of that merged set. Though metadata_t
is instantiated as follows -

metadata = new (std::nothrow) metadata_t (properties);

This can still throw. All std::nothrow guarantees is the allocation of
space for metadata_t will not throw, it does not guard against metadata_t's
ctor throwing (which can happen since it takes, and therefore must allocate
space for, a copy of properties).

This is perhaps somewhat academic since, in a low memory situation, you are
likely hosed anyway (and on Linux you will likely never see an exception
because of the OOM killer), but from a design standpoint the library
currently has the ability to propagate errors back up the call stack to a
client that can, in no way, do anything about them.

Should the user facing API wrap and catch any propagated exceptions, and
either return some error code or in the std::bad_alloc, do an alloc_assert
and terminate?

Another option that will address this case is to introduce a custom
allocator which fails via alloc_assert, and update all STL type definitions
to use that allocator.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.zeromq.org/pipermail/zeromq-dev/attachments/20150127/59edc0df/attachment.htm>

More information about the zeromq-dev mailing list