Hi Nico, The socket handles are ints to comply with POSIX specification.In case you need type-safe socket handles, it's easy to layer that on top of nanomsg. Actually, most language bindings are doing so.
Btw, one minor, but nice feature of ints as socket handles is that you can close a socket, then try to use it and get EBADF error. In ZeroMQ, where I've used pointers as handles, your program just crashes.
Martin On 03/09/13 23:32, Nico Wiliams wrote:
Hi, I read a blog post about nanomsg's design, linked from HN. I was quite pleased with everything I read except the use of small integers to identify nanomsg sockets, and the lack of any context handles. Small integers / no context handles -> easy for developers using nanomsg, because it's familiar from the BSD sockets API. But it's not really any more difficult to use an opaque handle instead: instead of 'int' use an incomplete struct pointer typedef as follows typdef struct nn_handle *nn_handle; NN_EXPORT nn_handle nn_socket (int domain, int protocol); NN_EXPORT int nn_close (nn_handle s); NN_EXPORT int nn_setsockopt (nn_handle s, int level, int option, const void *optval, size_t optvallen); ... The only change then is the type of 's' in these functions and the return value type for nn_socket(). Oh, and static type safety: there will be no way (short of casting) to get past a compiler error if one accidentally mixes real file descriptors with nn descriptors. Developers writing nanomsg apps should not really notice the difference. It's just a type name difference that changes nothing about how the values of that type are used. The 'nn_handle' type could even be a typedef of int (typedef int nn_handle). Given that the nn sockets are opaque anyways, why should the underlying type matter to nn callers? How can using an integer type help a developer in this case? Small integers imply a table lookup that is difficult to make fast and thread-safe. In an API where there is isolation between the caller and the callee (e.g., system calls) this is a good thing to have as a validation mechanism: kernels shouldn't dereference kernel memory addresses supplied by user-land callers! Integers still aren't needed for this, but it is easier to validate small integers than arbitrary pointers[*]. Looking at the code... right now nanomsg depends on global locks a bit too much, IMO, and much of this dependency has to do with the self.socks[] array initialization (and initialization and maintenance of self.nsocks, and self.unused, ...). At least some of which would go away if you used pointers instead of small integers to name nn sockets. Some of this could be optimized by using pthread_once() for initialization and judicious use of memory barriers and other lighter-weight synchronization primitives, but it's not trivial to get right. Whereas the socktypes and transports lists could be trivially initialized once in a pthread_once(), with no further synchronization being required (except for nn_term(), but I wonder if it's really desirable to allow applications to call nn_term(); seems like a job for .fini sections). Also there's a hard max on number of sockets, which can only be removed by either complicating the table lookup and/or lookup table management... or by not having to have a table in the first place. Respectfully, Nico [*] Note that a kernel-mode nanomsg implementation could use small integers cast back and forth to an opaque struct pointer anyways, so all in all I don't see how naming sockets after small integers helps.