[zeromq-dev] errno

Martin Sustrik sustrik at 250bpm.com
Sun Jan 15 02:26:08 CET 2012


Hi John,

> i am making a binding of 0MQ for the Felix language. I understand the 0MQ developers
> would like a Posix looking interface, so as to finally put into Posix what should have
> been there in the first place: a message passing API.

That could be seen as an ultimate goal - not sure whether POSIX 
standardisation work is going on still thought. From a brief examination 
it looks like it's more or less at hold at the moment.

> I'll post the API here when i'ts done.
> [The binding will be shorter than the zmq.h file!]

Nice!

> Cool! Good luck with Standards committees (me = former member of WG21) ;(

Some people here have been through AMQP standardisation committee. 
Pieter's article about standardisation-by-committee is worth reading IMO:

http://www.imatix.com/articles:whats-wrong-with-amqp

> C API
> ******
>
> However, I have an issue with this:
>
> "The zmq_errno() function shall retrieve the value of the errno variable for the calling thread.
> Users not experiencing issues with retrieving the correct value of errno should not use this
> function and should instead access the errno variable directly."
>
> and the whole API design, mimicking extremely bad practice found in Posix.

Ack. That's the world we are living in though.

> The core API should be reentrant. Attempts are being made for C and Posix
> to provide a re-entrant API. At least some functions have a fun_r variant
> for this purpose.
>
> Errno is one of the worst offenders.
>
> I think the right solution to this problem is probably like this:
>
>    errno_t fun_r( ... )  // core function returns error code
>    int fun(...) { return (errno = fun_r(...)) ? -1 : 0; }

Yes. That could work. The obvious problem are the functions that return 
a value (zmq_send, zmq_recv return number of bytes sent/recvd).

> All developers should use the re-entrant fun_r interface.
> The errno setting variant is only provided to make the interface look like Posix
> and is deprecated (as all the Posix functions doing this also should be).

The primary goal of POSIX interface in 0MQ is to make provide a flat 
learning curve for those already familiar with POSIX API.

Once we start to fix POSIX problems in 0MQ we'll diverge from POSIX API 
and miss the primary goal of the whole endeavour.

More discussion is needed about whether addressing POSIX problems is at 
all in the scope of 0MQ project.

> There is a related issue. Zmq provides zmq_strerror(int) to fetch a human readable
> message. That's nice. But it doesn't solve the following problem: 0MQ breaks Posix
> by "illegally" setting Posix errno to unspecified error codes. This has a serious
> consequence: the standard perror function no longer works.
> It's not even clear Posix allows you to write errno: it could be a macro,
> and it could be a TLS variable with special handling which might make it
> read-only to application code (unlikely but possible if the Posix/C library
> was specially mapped so the hardware could tell who's who).

True. However, it boils down to a practical problem: errno is available 
basically on all systems and setting it seems to work everywhere 
although it is not mandated by POSIX.

If we choose some other way of reporting errors, we'll diverge from 
POSIX and make the learning curve more steep. Basically the same problem 
as discussed above.

> C++ API
> ********
>
> Are you sure you want to throw an error any time a function doesn't work as expected?
> [That's a question :]

To be frank I've written C++ binding to make writing testing scripts 
easier. I.e. instead of placing "assert (rc == 0);" after each C 
invocation, the error it thrown automatically. That reduces test size by 
~50%.

Of course, if C++ binding is to be used for serious work, preferences 
may be different.

I guess actual users of C++ binding should speak up here.

> The reason I ask is: in C++, throwing exceptions is supposed to be for actual errors.
> Many Posix functions can return or result in result codes that indicate the function
> did not perform the required operation, but which are NOT errors but normal
> behaviour. For example EAGAIN is not an error, nor is EINTR: the client should re-try the operation.

Note that recv() function returns bool: true is message was received, 
false in case of EAGAIN.

Still, the C++ API is still a kind of draft. I would say, if you have an 
clear idea how it should look like, go on and implement it. The actual 
implementation work should't take more than an hour or so.

> Another example: open. So you opened a file you didn't have permissions
> to open or the file is locked. That does not mean its an error to try and fail.
> Throwing an exception here would be the wrong thing to do. Exceptions are evil,
> they should only be used to report a worse evil.  In some cases the C like API is better.
> [It would be even better if C++ had a real sum type .. but that just another reason
> I have developed Felix .. :]

Ha. Totally agreed. The 0MQ core is written in C++ but there's not a 
single exception used throughout the codebase.

> There's another issue with the C++ API: it's entirely inline. Please don't, especially
> constructors and destructors. Absolutely Verboten for class error_t! This class
> is "polymorphic" because it derives from the standard library exception class.
> At least ctors, dtors, and overrides of virtuals should be given in body, never
> in headers. The reason is "One Definition Rule" and "Dynamic Libraries".
> If you have separately compiled shared libs/DLLs and use this header,
> BOTH libs will necessarily "lay down" at least some RTTI, vtables, and probably
> functions in the library: they have to, they don't know about the other lib.
> Consequently, it is more or less impossible to catch such an exception, because
> it does not have a unique identity.
>
> if you are dynamic loading, there is ONLY one way to do this properly:
> the constructor and virtuals must be in a library **separate from all the others**.
> To enforce this .. just don't use inline functions.
>
> This argument applies to ALL classes. Production classes should have
> constructors and destructions and virtuals in the body, never in the headers.
> For zmq I'd put all of it in body, because most zmq functions are so heavy anyhow
> there's no point inlining a wrapper.
> [Note that this blows away templates. There is special syntax for making
> stuff build in a particular unit, but in ISO C++98 there's no solution for
> shared libs. I'm not sure what was done, if anything, in ISO C++11.]

The only reason why they are inline is not have to bother with the build 
system (C++ binding is a single header file).

+1 for your analysis. However, I have not enough free time to invest in 
the C++ binding. If you want to help with this, you are welcome!

Martin



More information about the zeromq-dev mailing list